I’m hoping that some of you could give some insight into a method for doing what I’m going to try to explain below.
I have a situation in my qnx based project were I would like multiple processes to register with another process. (A multi to one relationship). During normal operations messages will be sent from this single higher level process down to the “many” lower level processes. So at startup I want to have the lower level processes send a pulse/event upto the “server”, for lack of a better term. Once the server gets the pulse I would like to to send a message down to the sender (client) of that pulse to request its file path. (Note: all processes in question are Resource Managers). Once the clients return their file paths then the server will in the future be able to communicate with all the clients.
I know the server/client nomenclature probably isn’t being used properly here, but I just needed to name the processes something
My dilema is that I don’t know how to send the appropriate information in the pulse/event to the server, for it to be able to respond to the clients. A pulse does have 32 bits for “data”, but I don’t know if there is anything that I could put in there that would work.
I have also read that there is a new async messaging system that is “experimental” in 6.3.0 (we are using 6.3.2a). My assumption is that these messages could contain enough data to allow me to send the filepath from the client to the server with a single async message. Any thoughts on any/all of this would be appriciated.
You don’t have to send a pulse to notify the “server” that a “client” is there, just send a message with the full path. That creates possible dead lock, client would be allow to send the notification at startup and only once. (that could be a no brainer if the server and client are multi-threaded).
Other possibility is that if you control the path where the client lives and if you can all have them sit in a directory ( /dev/client/process1, /dev/client/process2, etc) or use some logic that makes them easy to find. The server could simply scan /dev/client either on a fixed timebased or upon receiving a pulse from the client requesting a directory scan to detect presence of new clients.
Shared memory or async messages are a possiblity but they will not work over network. in 6.3.2 async messagin is not experimental anymore ( I beleive ).
Other possibility is to have the client request to be notify (with the io_notify mecanism) by the server then when new data is available. Upon reception of the notification the client would send a message to the server which would reply with the data. This as one big advantages, when the server sends a message to a client, it will block until the client perform a reply. That means the “server” can be affected by the performance of the client. Let’s see the server has to send a message to 10 client at 100hz. No big deal right, but then the client goes to save a file on the disk, HD is busy for whatever reason and the write operation of the client takes 40ms. The server will be block of 40ms and NOT be able to respect the 100hz update cycle. Of course using notification is one way of fixing it, the other would be to have threads for each client in the servers, but this is not always simple.
Hope that helps.
Thanks mario for the reply. I was coming up with some of the same conclusions that you mentioned. I’m leaning toward the use of the first suggestion. I.e. sending messages in the wrong direction for registration. My logic is that only one registration message will ever be needed, and the server will never know how to talk to a client until a registration has been completed, so there is no possibility for a deadlock to occur. My only real reservation about it was if a client were to crash and be restarted by somebody, then it would attempt to register again… opening a window for deadlocks to occur. Granted I probably shouldn’t be designing clients to crash… so in theory this would never be a problem
Thanks for you insights!
The crash is not really an issue. If the server performs the open() on the client’s path only once and keep the file descriptor for futur use ( as oppose to doing an open() each time), then when the client dies that file descriptor will become invalid, any operation perform on it will return an error. Even if the client is restarted.
In fact if the server receives a registration request from an already register path it know that client was restarted.
I had thought about keeping the file descriptors open… for the reason you mentioned… I just wasn’t sure if there was anything “bad” about doing that?
I see a more fundamental question here. What are you trying to accomplish? From your description, you are trying to get data out to resource managers, but you don’t know if those managers exist, so you want them to register. Is that right? Do these resource managers process requests from any other processes (clients)? If not, there is a more straight forward way to design this. Instead of being resource managers, these processes can merely be worker clients. They “register” with the main resource manager by sending a message. The main resource manager doesn’t reply until it has work to do. It sends this request in the reply data. When the work is done, the worker sends a message indicating this, and again the main resource manager doesn’t reply unless it has work immediatly available.
This is a well worn structure that was used a lot in QNX 2 and QNX 4, however QNX 6 adds a new twist, which is multi-threading. The existing thread pool management doesn’t really cover this situation. If your worker processes are capable of multi-threading, here is a simple algorithm to provide the same features as thread pools.
On startup, a worker creates as many initial threads as required, and they each send a message to the resource manager. Whenever a thread gets a reply, it re-evaluates the thread number parameters. For example, when the number of of un-replied to (available) threads drops below some threshold, more are created. When a thread is ready to send a message saying it is available, but the number of available threads is large, it kills itself.
That last section is probably unnecessary as the usual reason for creating a work a process is to have a serially reusable resource. This structure isn’t as useful in QNX 6 because of threads, however it is easy to imagine an application where it would come in handy, eg. a communications environment where a node might be configured with various I/O channels, Async, Sync, SCSI, Firewire, etc. Each communication method would then have its own worker process, and the resource manager would have generic code.