Resource manager dispatching … using your own channels
Ever write a resource manager? Ever wonder why you were doing that and not just the raw operating system primitives?
If you have used Neutrino for any length of time, then you have probably done the former and if it was a trivial system, then the latter thought has probably crossed your mind.
QNX’s message passing strategy is all about following the Send/Receive/Reply mantra where clients talk to servers and block until they are replied to with a message. Under QNX4 there were officially no threads so when you were sending a message, you only needed to supply the node (transparent network message passing!) and the process identifier as routing information. When Neutrino (QNX6) was introduced, threads were made first class citizensand to make the targeting of message operations completely unambiguous, message channels were added. This meant that instead of just a node and process id, you also had to specify the channel identifier.
How does a client get these three pieces of information about the server it wants to talk to? There are lots of different schemes to accomplish this depending on the structure of the system you are building; ranging from sticking the information into a piece of shared memory to passing it from parent to child processes via a fork/exec/spawn type of operation. In a simple system, you can have simple sharing policies to get at this data, however as a system gets more complicated with more clients and servers, then things get out of hand quickly.
The preferred mechanism however, would be to let the OS do the heavy lifting and to replace the node, process id, channel id with a symbolic entry that is easy for clients to use and discover and let the resolution of that be handled by the operating system.
Enter the raison d’etre for the resource manager. It was introduced to help facilitate that binding on both the client (name resolution) and on the server (node, process id, channel id binding) as well as providing dispatching capabilities to help with the demultiplexing of common messages to specific handlers. The Neutrino Programmer’s Guide tells you all about this, and Rob Krten’s Neutrino books do a great job of providing further enlightenment.
About every six months or so I run into someone who is stuck in a very specific situation. They have created their own channel via ChannelCreate(), perhaps to turn off priority inheritance or because they need to avoid using certain flags, and would like to use it with the resource manager framework. The resource manager framework does such a good job of abstracting and hiding the channel that it seems that it isn’t possible.
Alas … do not lose hope! There is an undocumented function that allows this to occur:
dispatch_t *_dispatch_create(int chid, unsigned flags)
The function above allows you to pass in a channel identifier that you have created yourself and serves as a replacement for the general dispatch initialization function, dispatch_create(), that is the preferred initialization. As always, when you are using something that is undocumented (but publicly exposed) there are some caveats to consider:
- The flags field should be passed as 0 at this time
- In order for proper operation of the resource manager, you must specify _NTO_CHF_UNBLOCK | _NTO_CHF_DISCONNECT as flags for the channel. Without these flags, your resource manager may fail to properly close off connections from clients that abnormally disconnect.
One of the situations where this function is required, is if you are trying to have a resource manager as part of an application that also uses Photon. While this joining of server and interface functionality is generally frowned upon (a clean separation is always preferred) there are some situations where it simply isn’t practical. In this case, Photon generally grabs the _NTO_CHF_COID_DISCONNECT flag for itself, which the current resource manager framework assumes it will be able to use. Since this flag can be set on only one channel per process, your resource manager will end up failing during initialization with a mysterious error … a future release will clean this up and allow everyone to play nice, but for the current 6.3.x releases of Neutrino, you will need to use this function to work around that situation.