Are resource managers the hammer for every nail?

I’m just getting in to QNX6 and have been reading Rob Krten’s book Getting Started with QNX Neutrino 2 in order to… get… started.

In the chapter about Using Message Passing, when discussing how to find a server’s ND/PID/CHID, he strongly recommends using a resource manager to claim a piece of the path space as the best way to create a well-known server. Sounds resonable.

Later, in the chapter on Resource Managers he again strongly recommends using only the standard POSIX functions within the resource manager. When discussing the other_func handler he suggests using devctl for anything not covered by the basic file system calls. Again, this sounds reasonable. And a bit like Plan 9. And kind of REST-y for that matter.

But… I’m not convinced I should buy in to it. The first service I want to write is a central, in memory blackboard so that several other processes can share data. It should be hierarchical, so the filesystem organization makes sense. But it seems like there is a lot of potential for unnecessary overhead.

Consider an instrument like the GPS. It would make sense for the GPS’s data to be mapped to something like this:

/blackboard/gps/northings
/blackboard/gps/eastings
/blackboard/gps/connected_satellites

But now, to get all the data a client will need to open 3 files. (Or 12 files if I copy all of the interface fields.)

And, I would like to notify clients when data they are interested in has been changed. Either by sending a pulse to them or making their read() calls block until the data changes. This doesn’t seem to be possible strictly within the POSIX filesystem spec.

Have I missed something? Am I misunderstanding the spirit of Rob’s suggestions? Any advice or opinions would be great!

Dave

Sure it is, you can use poll/select. Also, there is no reason that you can’t use MsgSend() with an fd obtained by calling open().

Yes, it is a fairly common newbie misconception that resource managers are somehow mutually exclusive with MsgSend/Receive/Reply; in fact, resource managers are simply a higher level abstraction of MsgSend/Receive/Reply and you can freely mix both concepts (if you aren’t concerned with portability of course).

Rennie

Ah! select(). Thanks for the suggestion. That makes a lot of sense.

I see. I guess I’m looking for some design advice on when Resource Managers are appropriate and when the lower-level MsgSend/Receive/Reply calls are more appropriate. It certainly is nice to have the flexibility. But with great power comes great responsibility, and mixing them freely in the same process (which I’m already tempted to do) sounds like it would be messy.

The advice I’ve seen so far seems to say “use Resource Managers for everything! Use the low-level stuff only if you’re porting QNX4 code to Neutrino.” But the design behind Resource Managers seems to be appropriate only if there is a need for portability.

Using the Resource Manager attach calls to hook a path, but then not implementing any of the read()/write() etc. functionality seems a bit unusual. I would be agreeing to implement a contract, but then just returning an error from all the methods that are part of that contract. And then having this other interface using MsgSend/Receive/Reply that’s “the real thing.”

Since design advice is always tough to give without understanding the problem, let me describe my application.

I’m working on a small robot that would have GPS and range-finding (sonar and laser) sensors. It will explore a city park. In other words, it’s a RoboMagellan entry. Portability to non-QNX Environments is not a requirement.

There are three different types of software processes that I need to write. Low-level device drivers – clear candidates for resource managers. Higher level “protocol drivers” that speak a specific serial protocol on a serial port – maybe resource managers, but maybe they should just leave that to the serial port RM. And, the intelligence agents, that will only speak to drivers and each other – probably don’t need to be resource managers.

So, does that sort of reasoning make sense? I really appreciate the advice of all the more experienced folks around here.

Dave

Actually, it isn’t messy at all (as long as you don’t try to replicate read/write using MsgSend - even then it isn’t really messy - just unnecessary).

Actually, resource managers (RMs) themselves aren’t portable, but they lend portability to your application code. If portability of the application code is not important, then using the RM framework and using messages with it, is perfectly fine.

True; but the contract you would be breaking is the contract that says “derived works will conform strictly to the Posix open/read/write/close interface”. This contractual obligation is not part of the QNX RM contract (almost all callbacks can legally be NULL). There may be a burden of Posix conformance imposed upon you from whoever is driving your products requirements, but the RM contract clearly states (through the legality of NULL callbacks) that you are not required to provide implementations for those interfaces.

Music to my ears!

Very good characterization and understanding. Your observation that the “protocol drivers” don’t necessarily need to be RMs is astute; but again, I think you might be overloading the term RM with the term “Posix conforming RM”. In order to make this clear, let’s refer to “Posix conforming RMs” as PRMs and general server processes that just so happen to link against the resmgr library as SRMs (Server Resource Managers).

I would completely agree that most “device drivers” would be PRMs.

Further, I would then say that most “protocol drivers” would be SRMs

At this point I would caution against the idea that intelligence agents (IA) should “talk to each other”. A native QNX design should be layered such that no threads talk to “peer” threads (this is covered in the QNX docs). Instead I would suggest that the category of IA is further broken down into “intelligence service agents” (ISAs) and “intelligence client agents” (ICAs). ISAs would be implementations of SRMs (just like “protocol drivers” - in fact, the difference between an ISA and a “protocol driver” is contrived - though quite possibly useful in lexicon of your application).

ICAs would never send to each other, only to ISAs. ISAs would never send to ICAs, but could send to other ISAs; but not ISAs at the same “level” (see QNX docs for explanation of “levels”)

The architecture of device drivers and protocol drivers is fairly obvious as it is a model used by many operating systems. The architecture within the IA (i.e. the ICAs and ISAs) category is where you need to put your “QNXthink” cap on, in order to create a synchronous architecture that is non-deadlocking, and that exhibits a high degree of concurrency. It is actually very easy, but you must resist the urge to resolve potential deadlock problems by making an interface non-blocking, and instead resolve the issue by correcting the architecture (you have to trust me on this, only trust; or many years of painful experience will convince you of this truth - sort of like Dads advice “don’t get your girlfriend pregnant” - pretty obvious after years of child support payments and a screwed up kid - but difficult to adhere to when you’re young and that darned async messaging^H^H^H^H^H^H^H^H^H^H^H^H^H^H^Hgirl looks so inviting after a few beers). No, I am not talking from experience here - one of the best things I ever did was follow “Dads” advice - both literally and metaphorically - in this regard).

I will refrain from going on about “the lie of non-blocking interfaces”; suffice to say, that there is no such thing as a non-blocking interface, only interfaces that block predictably (synchronous IPC) and interfaces that block unpredictably (asynchronous IPC). I prefer interfaces that block predictably (deterministically).

You are most welcome.