Bug Summary

File:io/io_reactor.cpp
Location:line 181, column 2
Description:Potential memory leak

Annotated Source Code

1// This file is part of Playslave-C++.
2// Playslave-C++ is licenced under the MIT license: see LICENSE.txt.
3
4/**
5 * @file
6 * Implementation of the non-virtual aspects of the IoReactor class.
7 * @see io/io_reactor.hpp
8 */
9
10#include <csignal>
11#include <string>
12
13extern "C" {
14#include <uv.h>
15}
16
17#include "../cmd.hpp"
18#include "../errors.hpp"
19#include "../messages.h"
20#include "../player/player.hpp"
21#include "io_reactor.hpp"
22#include "io_response.hpp"
23
24const std::uint16_t IoReactor::PLAYER_UPDATE_PERIOD = 5; // ms
25
26//
27// libuv callbacks
28//
29// These should generally trampoline back into class methods.
30//
31
32struct WriteReq {
33 uv_write_t req;
34 uv_buf_t buf;
35};
36
37/// The function used to allocate and initialise buffers for client reading.
38void UvAlloc(uv_handle_t *, size_t suggested_size, uv_buf_t *buf)
39{
40 *buf = uv_buf_init(new char[suggested_size](), suggested_size);
41}
42
43/// The callback fired when a client connection closes.
44void UvCloseCallback(uv_handle_t *handle)
45{
46 if (handle->data != nullptr) {
47 auto tcp = static_cast<TcpResponseSink *>(handle->data);
48 tcp->Close();
49 }
50 delete handle;
51}
52
53/// The callback fired when some bytes are read from a client connection.
54void UvReadCallback(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
55{
56 TcpResponseSink *tcp = static_cast<TcpResponseSink *>(stream->data);
57 tcp->Read(stream, nread, buf);
58}
59
60/// The callback fired when a new client connection is acquired by the listener.
61void UvListenCallback(uv_stream_t *server, int status)
62{
63 if (status == -1) {
64 return;
65 }
66 IoReactor *reactor = static_cast<IoReactor *>(server->data);
67 reactor->NewConnection(server);
68}
69
70/// The callback fired when a response has been sent to a client.
71void UvRespondCallback(uv_write_t *req, int)
72{
73 // TODO: Handle the int status?
74 WriteReq *wr = (WriteReq *)req;
75 delete[] wr->buf.base;
76 delete wr;
77}
78
79/// The callback fired when the update timer fires.
80void UvUpdateTimerCallback(uv_timer_t *handle)
81{
82 Player *player = static_cast<Player *>(handle->data);
83 bool running = player->Update();
84 if (!running) {
85 uv_stop(uv_default_loop());
86 }
87}
88
89//
90// IoReactor
91//
92
93void IoReactor::NewConnection(uv_stream_t *server)
94{
95 uv_tcp_t *client = new uv_tcp_t();
96 uv_tcp_init(uv_default_loop(), client);
97
98 if (uv_accept(server, (uv_stream_t *)client) == 0) {
99 auto tcp = std::make_shared<TcpResponseSink>(*this, client,
100 this->handler);
101 this->player.WelcomeClient(*tcp);
102 this->connections.insert(tcp);
103 client->data = static_cast<void *>(tcp.get());
104
105 uv_read_start((uv_stream_t *)client, UvAlloc, UvReadCallback);
106 } else {
107 uv_close((uv_handle_t *)client, UvCloseCallback);
108 }
109}
110
111void IoReactor::RemoveConnection(TcpResponseSink &conn)
112{
113 this->connections.erase(std::make_shared<TcpResponseSink>(conn));
114}
115
116IoReactor::IoReactor(Player &player, CommandHandler &handler,
117 const std::string &address, const std::string &port)
118 : player(player), handler(handler)
119{
120 InitAcceptor(address, port);
121 DoUpdateTimer();
122}
123
124void IoReactor::Run()
125{
126 uv_run(uv_default_loop(), UV_RUN_DEFAULT);
127}
128
129void IoReactor::DoUpdateTimer()
130{
131 uv_timer_init(uv_default_loop(), &this->updater);
132 this->updater.data = static_cast<void *>(&this->player);
133 uv_timer_start(&this->updater, UvUpdateTimerCallback, 0,
134 PLAYER_UPDATE_PERIOD);
135}
136
137void IoReactor::InitAcceptor(const std::string &address,
138 const std::string &port)
139{
140 int uport = std::stoi(port);
141
142 uv_tcp_init(uv_default_loop(), &this->server);
143 this->server.data = static_cast<void *>(this);
144
145 struct sockaddr_in bind_addr;
146 uv_ip4_addr(address.c_str(), uport, &bind_addr);
147 uv_tcp_bind(&this->server, (const sockaddr *)&bind_addr, 0);
148
149 // TODO: Handle errors from uv_listen.
150 uv_listen((uv_stream_t *)&this->server, 128, UvListenCallback);
151}
152
153void IoReactor::RespondRaw(const std::string &string) const
154{
155 for (const auto &conn : this->connections) {
156 conn->RespondRaw(string);
157 }
158}
159
160void IoReactor::End()
161{
162 uv_stop(uv_default_loop());
163}
164
165//
166// TcpResponseSink
167//
168
169TcpResponseSink::TcpResponseSink(IoReactor &parent, uv_tcp_t *tcp,
170 CommandHandler &handler)
171 : parent(parent), tcp(tcp), tokeniser(handler, *this)
172{
173}
174
175void TcpResponseSink::RespondRaw(const std::string &string) const
176{
177 unsigned int l = string.length();
178 const char *s = string.c_str();
179
180 WriteReq *req = new WriteReq;
181 req->buf = uv_buf_init(new char[l + 1], l + 1);
1
Memory is allocated
2
Potential memory leak
182 memcpy(req->buf.base, s, l);
183 req->buf.base[l] = '\n';
184
185 uv_write((uv_write_t *)req, (uv_stream_t *)tcp, &req->buf, 1,
186 UvRespondCallback);
187}
188
189void TcpResponseSink::Read(uv_stream_t *stream, ssize_t nread,
190 const uv_buf_t *buf)
191{
192 if (nread < 0) {
193 if (nread == UV_EOF) {
194 uv_close((uv_handle_t *)stream, UvCloseCallback);
195 }
196
197 return;
198 }
199
200 if (buf->base != nullptr) {
201 this->tokeniser.Feed(buf->base, nread);
202 delete[] buf->base;
203 }
204}
205
206void TcpResponseSink::Close()
207{
208 Debug() << "Closing client connection" << std::endl;
209 this->parent.RemoveConnection(*this);
210}