What do iOS and Android do?
I don’t know what Android does; iOS eventually uses Apple’s CFRunLoop, which isn’t a select()able object (and maybe we can wrap select()able things in CFWhatevers, but now it’s getting complicated).
I think the best way to do this in a cross-platform way, using as low “power” as possible, and compatibile with every SDL target is to spawn a new thread and do your network I/O there with a select() call or if you have a single socket with blocking send()/recv(), then you can forward to the SDL loop your events using SDL_USEREVENT.
The only penality I see is that to avoid using semaphores or other locking methods you’ll have to allocate a buffer for every packet you receive to be passed in your SDL_UserEvent struct (the SDL main thread will free the buffer once it processes it).
Actually you don’t. It can work like this:
(a) client allocates a buffer and intends it be filled from some socket
(b) client posts request to I/O thread
© I/O thread monitors socket and fills buffer
(d) I/O threads posts completion notice to client
Step (d) can use SDL event queue which is already thread safe, so no (additional)
locking is required. Step (b) requires some protocol such as a thread safe
request queue to send the request. Although this can be done with locks
it isn’t necessary if you have CAS: queues can be done without locks.
This actually suggests SDL could use a thread safe queue for portability.
This is a nice idea but often not that usable. Specifically the server
might send data to the client at any time. Like “player foo has moved
unit bar to baz”. The client would have to blindly allocate buffers
and request IO in the assumption that something probably will happen.
I think a better design is for the I/O thread to allocate buffers as
needed, fill them and pass them to the main thread. The main thread
can then either free them when it is done or hand them back to the I/O
thread for reuse.
Of course select is a pretty bad way to monitor sockets.
Might be OK for a game client but I’d hate to use it for a server ;(
You would be using poll or epoll where available for scalability
reasons on a server.
But on a client where you probably only have one socket (and a pipe,
see below) select is perfectly fine.
I’ve implemented the solution with a GUI thread and an IO thread. But
that exposed another kind of problem. The socket can close and then
the IO thread sends a signal to the GUI thread via the sdl event loop.
But the GUI can close too (user hitting the close button). Then the
GUI thread terminates while the IO thread remains stuck in the select
loop.
Usualy the OS has some way to kill threads or send signals to abort
the select loop. But that seems to have protability problems. So I
came up with another simple solution: a pipe.
At the start I create a pipe, named kill_pipe. The reading side goes
to the IO thread while the writing side remains with the GUI thread.
The IO thread includes the pipe in the select/poll/epoll call waiting
for read to be possible. When quiting the GUI thread simply closes the
writing end of the pipe. This causes the reading end of the pipe to
become ready for read and wakes up the select/poll/epoll. And the IO
thread detects that the FD ready to read is the kill_pipe and quits
too.
The same pipe could also be used to send other things, like to return
buffers. Simply write the address of the buffer to it and let the IO
thread read them back. Only on EOF on the pipe the IO thread would
terminate. Instant threadsafe queue.
MfG
GoswinOn Tue, Jul 09, 2013 at 10:02:58PM +1000, john skaller wrote:
On 09/07/2013, at 6:06 PM, Gabriele Greco wrote: