.\" -*-nroff-*- .TH sel 3 "22 May 1999" mLib .SH NAME sel \- low level interface for waiting for I/O .\" @sel_init .\" @sel_initfile .\" @sel_addfile .\" @sel_rmfile .\" @sel_addtimer .\" @sel_rmtimer .\" @sel_select .SH SYNOPSIS .nf .B "#include " .BI "void sel_init(sel_state *" s ); .BI "void sel_initfile(sel_state *" s ", sel_file *" f , .BI " int " fd ", unsigned " mode , .BI " void (*" func ")(int " fd ", unsigned " mode ", void *" p ), .BI " void *" p ); .BI "void sel_addfile(sel_file *" f ); .BI "void sel_rmfile(sel_file *" f ); .BI "void sel_addtimer(sel_state *" s ", sel_timer *" t , .BI " struct timeval *" tv , .BI " void (*" func ")(struct timeval *" tv ", void *" p ), .BI "void sel_rmtimer(sel_timer *" t ); .BI "int sel_select(sel_state *" s ); .fi .SH "OVERVIEW" The .B sel subsystem provides a structured way of handling I/O in a non-blocking event-driven sort of a way, for single-threaded programs. (Although there's no reason at all why multithreaded programs shouldn't use .BR sel , it's much less useful.) .PP The .B sel subsystem does no memory allocation, and has no static state. All of its data is stored in structures allocated by the caller. I'll explain how this fits in nicely with typical calling sequences below. .PP Although all the data structures are exposed in the header file, you should consider .BR sel 's data structures to be opaque except where described here, and not fiddle around inside them. Some things may become more sophisticated later. .SH "IMPORTANT CONCEPTS" The system is based around two concepts: .I multiplexors and .IR selectors . .PP A .I selector is interested in some sort of I/O event, which might be something like `my socket has become readable', or `the time is now half past three on the third of June 2013'. It has a handler function attached to it, which is called when the appropriate event occurs. Some events happen once only ever; some events happen over and over again. For example, a socket might become readable many times, but it's only half-past three on the third of June 2013 once. .PP When a selector is initialized, the caller describes the event the selector is interested in, and specifies which function should handle the event. Also, it must specify an arbitrary pointer which is passed to the handler function when the event occurs. This is typically some sort of pointer to instance data of some kind, providing more information about the event (`it's .I this socket that's become readable'), or what to do about it. .PP A multiplexor gathers information about who's interested in what. It maintains lists of selectors. Selectors must be added to a mulitplexor before the events they're interested in are actually watched for. Selectors can be removed again when their events aren't interesting any more. Apart from adding and removing selectors, you can .I select on a multiplexor. This waits for something interesting to happen and then fires off all the selectors which have events waiting for them. .PP You can have lots of multiplexors in your program if you like. You can only ask for events from one of them at a time, though. .PP There are currently two types of selector understood by the low-level .B sel system: file selectors and timer selectors. These two types of selectors react to corresponding different types of events. A file event indicates that a file is now ready for reading or writing. A timer event indicates that a particular time has now passed (useful for implementing timeouts). More sophisticated selectors can be constructed using .BR sel 's interface. For examples, see .BR selbuf (3) and .BR conn (3). .SH "PROGRAMMING INTERFACE" A multiplexor is represented using the type .B sel_state defined in the .B header file. Before use, a .B sel_state must be initialized, by passing it to the .B sel_init function. The header file talks about `state blocks' a lot \- that's because it was written before I thought the word `multiplexor' was nicer. .PP File selectors are represented by the type .BR sel_file . The interface provides three operations on file selectors: initialization, addition to multiplexor, and removal from a multiplexor. It's convenient to separate addition and removal from initialization because file selectors often get added and removed many times over during their lifetimes. .PP A file selector is initialized by the .B sel_initfile function. This requires a large number of arguments: .TP .I s A pointer to the multiplexor with which the file selector will be associated. This is stored in the selector so that the multiplexor argument can be omitted from later calls. .TP .I f Pointer to the file selector object to be initialized. .TP .I fd The file descriptor which the selector is meant to watch. .TP .I mode A constant describing which condition the selector is interested in. This must be one of the .B SEL_ constants described below. .TP .I func The handler function which is called when the appropriate condition occurs on the file. This function's interface is described in more detail below. .TP .I p An arbitrary pointer argument passed to .I func when it's called. Beyond this, no meaning is attached to the value of the pointer. If you don't care about it, just leave it as null. .PP The mode argument is one of the following constants: .TP .B SEL_READ Raise an event when the file is ready to be read from. .TP .B SEL_WRITE Raise an event when the file is ready to be written to. .TP .B SEL_EXC Raise an event when the file has an `exceptional condition'. .PP The constant .B SEL_MODES contains the number of possible file modes. This is useful internally for allocating arrays of the right size. .PP The functions .B sel_addfile and .B sel_rmfile perform the addition and removal operations on file selectors. They are passed only the actual selector object, since the selector already knows which multiplexor it's associated with. A newly initialized file selector is not added to its multiplexor: this must be done explicitly. .PP The handler function for a file multiplexor is passed three arguments: the file descriptor for the file, a mode argument which descibes the file's new condition, and the pointer argument set up at initialization time. .PP The member .B fd of the .B sel_file structure is exported. It contains the file descriptor in which the selector is interested. You may not modify this value, but it's useful to be able to read it out \- it saves having to keep a copy. .PP Timer selectors are simpler. There are only two operations provided on timer selectors: addition and removal. Initialization is performed as part of the addition operation. .PP A timer selector is represented by an object of time .BR sel_timer . .PP The function .B sel_addtimer requires lots of arguments: .TP .I s Pointer to the multiplexor to which the selector is to be added. .TP .I t Pointer to the timer selector object being initialized and added. .TP .I tv A .B "struct timeval" object describing when the selector should raise its event. This is an .I absolute time, not a relative time as required by the traditional .BR select (2) and .BR poll (2) system calls. .TP .I func A handler function to be called when the event occurs. The function is passed the .I current time, and the arbitrary pointer passed to .B sel_addtimer as the .I p argument. .TP .I p A pointer passed to .I func when the timer event occurs. Beyond this, the value of the pointer is not inspected. .PP The function .B sel_rmtimer removes a timer selector. It is passed only the selector object. .PP Note that timer events are a one-shot thing. Once they've happened, the timer selector is removed and the event can't happen again. This is normally what you want. Removing a timer is only useful (or safe!) before the timer event has been sent. .PP Finally, the function .B sel_select is passed a multiplexor object. It waits for something interesting to happen, informs the appropriate selector handlers, and returns. If everything went according to plan, .B sel_select returns zero. Otherwise it returns -1, and the global variable .B errno is set appropriately. .SH "OTHER NOTES" Although the naming seems to suggest that this is all based around the BSD-ish .BR select (2) system call (and indeed it is), the interface is actually a good deal more general than that. An implementation which worked off System V-ish .BR poll (2) instead would be fairly trivial to make, and would look just the same from the outside. .SH "SEE ALSO" .BR select (2), .BR poll (2), .BR mLib (3). .SH AUTHOR Mark Wooding,