using select() without blocking the runloop


Gerriet M. Denkmann
 

I need to do select() with a rather large timeout value (several minutes).

So in order not to block the runLoop / mainThread I tried:

dispatch_queue_t myQueue = dispatch_get_global_queue( QOS_CLASS_BACKGROUND, 0 );
dispatch_async( myQueue, ^void(void)
{
selectResult = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
selectErrno = errno;
}
);

But I get EINVAL = Invalid argument.
Seems like file descriptors are thread specific.

How to do an asynchronous select() ?

Gerriet.


Roland King
 

yes of course file handles are thread-specific, you wouldn’t want random bits of code trying 0, 1, 2 … to see what it could get. Actually they are process-specific, but the process running your dispatch queue threads is not you and even if it was you the threads were probably created before you created the filehandles so it wouldn’t have inherited them.

the easiest way is to use a normal thread, start it, select on it, finish it again.

if you want to be properly dispatch_* about it, create a READ (or WRITE) dispatch source with the file descriptor and queue. Then add the handler block and resume it. To do the same thing the select() timeout does you can install a dispatch_after to cancel it again. It’s annoyingly more code but that’s how libdispatch supports filehandles natively. You could easily wrap that in a class and just use when required.

On 24 Nov 2017, at 16:57, Gerriet M. Denkmann <g@mdenkmann.de> wrote:

I need to do select() with a rather large timeout value (several minutes).

So in order not to block the runLoop / mainThread I tried:

dispatch_queue_t myQueue = dispatch_get_global_queue( QOS_CLASS_BACKGROUND, 0 );
dispatch_async( myQueue, ^void(void)
{
selectResult = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
selectErrno = errno;
}
);

But I get EINVAL = Invalid argument.
Seems like file descriptors are thread specific.

How to do an asynchronous select() ?

Gerriet.




James Walker
 

On Nov 24, 2017, at 12:57 AM, Gerriet M. Denkmann <g@mdenkmann.de> wrote:

I need to do select() with a rather large timeout value (several minutes).

So in order not to block the runLoop / mainThread I tried:

dispatch_queue_t myQueue = dispatch_get_global_queue( QOS_CLASS_BACKGROUND, 0 );
dispatch_async( myQueue, ^void(void)
{
selectResult = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
selectErrno = errno;
}
);

But I get EINVAL = Invalid argument.
Seems like file descriptors are thread specific.

How to do an asynchronous select() ?
I don’t think file descriptors are thread specific.

Most of your parameters to select are pointers. But what do they point to? If they point to structures on the stack, that would be bad, because those structures will be gone by the time the select runs.


Jack Brindle
 

If file handlers were thread-specific you would not be able to read files on other threads. They are process-specific, not thread-specific.

At its core, select is very much synchronous. This haunted the Mac file system until the network folks started using KQueues to handle the descriptors. GCD made this much easier with dispatch sources based on KQueue elements. What I suspect you really want to do is to create a dispatch_source using DISPATCH_SOURCE_TYPE READ and DISPATCH_SOURCE_TYPE_WRITE. so that you don’t block, instead letting the system do the chore for you. Apple has some pretty good docs on this in their Dispatch guide. It is worth reading. They also have (or at least used to) an interesting sample source that demonstrates the use of various dispatch source features, done in command-line type programs. A search for it is definitely worth while.

This isn’t all that difficult to do, and if you really need to use select() is by far the best way to go.

- Jack

On Nov 24, 2017, at 1:36 AM, Roland King <rols@rols.org> wrote:

yes of course file handles are thread-specific, you wouldn’t want random bits of code trying 0, 1, 2 … to see what it could get. Actually they are process-specific, but the process running your dispatch queue threads is not you and even if it was you the threads were probably created before you created the filehandles so it wouldn’t have inherited them.

the easiest way is to use a normal thread, start it, select on it, finish it again.

if you want to be properly dispatch_* about it, create a READ (or WRITE) dispatch source with the file descriptor and queue. Then add the handler block and resume it. To do the same thing the select() timeout does you can install a dispatch_after to cancel it again. It’s annoyingly more code but that’s how libdispatch supports filehandles natively. You could easily wrap that in a class and just use when required.

On 24 Nov 2017, at 16:57, Gerriet M. Denkmann <g@mdenkmann.de> wrote:

I need to do select() with a rather large timeout value (several minutes).

So in order not to block the runLoop / mainThread I tried:

dispatch_queue_t myQueue = dispatch_get_global_queue( QOS_CLASS_BACKGROUND, 0 );
dispatch_async( myQueue, ^void(void)
{
selectResult = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
selectErrno = errno;
}
);

But I get EINVAL = Invalid argument.
Seems like file descriptors are thread specific.

How to do an asynchronous select() ?

Gerriet.






 



On Nov 24, 2017, at 1:36 AM, Roland King <rols@...> wrote:

Actually they are process-specific, but the process running your dispatch queue threads is not you

That’s not true. All threads accessible to you are by definition running in your process. Dispatch queues run on regular threads in your process just like everything else.

—Jens


 



On Nov 24, 2017, at 12:57 AM, Gerriet M. Denkmann <g@...> wrote:

I need to do select() with a rather large timeout value (several minutes).
So in order not to block the runLoop / mainThread I tried:

Don’t use dispatch queues for long blocking operations. It messes up libDispatch’s scheduling. For a task like this, you should explicitly create a thread using either NSThread or pthreads.

(Also, there’s almost never a need to use select() in Cocoa code. Is there a reason you can’t use any higher level network code like CFStream, NSStream, or NSURLSession?)

dispatch_queue_t myQueue = dispatch_get_global_queue( QOS_CLASS_BACKGROUND, 0 );
dispatch_async( myQueue, ^void(void)
{
selectResult = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);

The likely problem here is that several of these parameters are pointers, and the buffers they point to may no longer be valid by the future time that the block runs … especially if they’re local variables of the enclosing function/method/

—Jens


Chris Hanson
 

On Nov 24, 2017, at 1:36 AM, Roland King <rols@...> wrote:

yes of course file handles are thread-specific, you wouldn’t want random bits of code trying 0, 1, 2 … to see what it could get. Actually they are process-specific, but the process running your dispatch queue threads is not you and even if it was you the threads were probably created before you created the filehandles so it wouldn’t have inherited them. 

Neither of these statements is correct:

(1) File handles are per-process, not per-thread. It’s the developer’s responsibility not to try to manipulate the same file handle from multiple threads at the same time. They’re shared across a fork() and can be shared across a posix_spawn(), which is how stdin/stdout/stderr is set up in a child process by its parent, but in general this isn’t something an application developer has to worry about. (NSTask deals with all of the gory details of ensuring all of your descriptors don’t get shared to any spawned tasks, only the stdin/stdout/stderr you set.)

(2) Dispatch queue threads are not run in a different process than they’re used from. This doesn’t even really make sense, since if they were in different processes they wouldn’t be sharing memory without using some explicit shared-memory API, and “__block” doesn’t do that.

  -- Chris