Start the resource manager message loop
/* start the resource manager message loop */
while(1) {
if((ctp = dispatch_block(ctp)) == NULL) {
fprintf(stderr, "block error\n");
return EXIT_FAILURE;
}
dispatch_handler(ctp);
}
Once the resource manager establishes its name, it receives messages when any client program tries to perform an operation (e.g., open(), read(), write()) on that name.
In our example, once /dev/sample is registered, and a client program executes:
fd = open ("/dev/sample", O_RDONLY);
the client's C library constructs an _IO_CONNECT message and sends it to our resource manager. Our resource manager receives the message within the dispatch_block() function. We then call dispatch_handler(), which decodes the message and calls the appropriate handler function based on the connect and I/O function tables that we passed in previously. After dispatch_handler() returns, we go back to the dispatch_block() function to wait for another message.
- If dispatch_block() returns a non-NULL context pointer, it could be different from the one passed in, as it's possible for the ctp to be reallocated to a larger size. In this case, the old ctp is no longer valid.
- If dispatch_block() returns NULL
(for example, because a signal interrupted the MsgReceive()),
the old context pointer is still valid.
Typically, a resource manager targets any signals to a thread that's dedicated to handling signals.
However, if a signal can be targeted to the thread doing
dispatch_block(), you could use the following code:
dispatch_context_t *ctp, *new_ctp; ctp = dispatch_context_alloc( … ); while (1) { new_ctp = dispatch_block( ctp ); if ( new_ctp ) { ctp = new_ctp } else { /* handle the error condition */ … } }
At some later time, when the client program executes:
read (fd, buf, BUFSIZ);
the client's C library constructs an _IO_READ message, which is then sent directly to our resource manager, and the decoding cycle repeats.
