On Tuesday 07 February 2006 00:13, Andrew Johnson wrote:
> Reconciling the synchronous dbGetLink() call API with the
> asynchronous record processing behaviour would require that
> dbGetLink() not return until after the I/O operation has completed,
> which ties up the task that is doing the dbGetLink() operation - it
> can't return until it knows the value is available. We don't know
> how long that will take though, it may be half an hour for some
> devices (extreme, but legal). Since it's obviously not acceptable to
> suspend any of the existing scan tasks for that period of time
> waiting for one device to finish, we have to accept that the current
> design doesn't allow us to implement the behaviour we'd like for PP
> links to asynchronous records. [Ben, if you think you have a working
> solution I'd love to see it.]
Hi Andrew (and whoever else is interested),
I have a working solution. It was a little bit more work than I expected
at first. As far as I can see at the moment, the basic idea is sound,
and the code works as specified. The attached file contains the patched
source files, which are based on R184.108.40.206. I also attached a text file
that explains solution and limitations.
Let me know what you think.
The following explains the idea behind the new asynchronous getLink
The first problem to solve is compatibility. Old databases and existing
record types should work in the same way as before. Particularly, regular
PP input links must work synchronously as before.
Therefore we need a new link option for input links, which I have named
'APP' for 'Asynchronous Process Passive'. (This was the easiest part.)
Note that, just as with the old PP input links, 'APP' is honored only if
the target record is on the same IOC, i.e. if the link really is a DB
link and not an implicit CA link. Also, 'APP' is honored only if the new
dbGetLinkCallback routine is used (see below).
[Aside: There is a design choice here: currently, the old dbGetLink does
not recognize APP, i.e. it is handled as NPP; it would also be possible
to treat it as PP. This can be easily changed.]
The second (and harder) problem is how to design the interface so that
record supports can be adapted without too much effort. I experimented
with a number of variants, and in the end arrived at a very simple
solution. There is one new dbAccess routine:
long dbGetLinkCallback(struct link *, short dbrType, void *pbuffer,
long *options, long *nRequest);
As you see, it has exactly the same arguments as dbGetLink. What differs
is the semantics.
First, if the link type is not DB_LINK, or else if the APP option is not
set, or else if the record containing the link has PACT set, then it
simply calls the old dbGetLink.
Otherwise (i.e. if the link type is DB_LINK, and APP option is set, and
calling record's PACT is FALSE) it sets PACT and calls dbScanPassive to
process the target record. Afterwards, it checks if the target record's
PACT is set. If yes, arranges for a process callback to be issued when
the target record completes processing, and then returns; else (target's
PACT is FALSE) resets calling record's PACT to FALSE and calls dbGetLink.
This scheme ensures that asynchronous callbacks are issued only if
necessary, i.e. if the target record really is asynchronous.
[A note on implementation: I added two new fields to dbCommon, named PGNL
(Pending GetLinkNotifies) and GLNR (GetLinkNotifyRecord). These are
pointer fields that get allocated on first use and are never freed. PGNL
is used to maintain a list of pending process callbacks for records that
have requested asynchronous getLink. GLNR is where the list nodes
(including the callback structure itself) are stored.]
User code (mainly record support) proceeds in the same way as with
asynchronous device supports: it saves PACT, calls dbGetLinkCallback, and
checks whether PACT was changed from FALSE to TRUE. If yes, than it
returns immediately (to be called back again). Otherwise, we are either
in the asynch completion phase or the target record was not asynchronous
or the link was not APP. In all these cases we can proceed just as after
a normal dbGetLink, i.e. (provided the status returned was OK) we really
have retrieved the data from the link.
As a proof of concept, I converted the aoRecord support to use the new
dbGetLinkCallback. This was the hardest part, partly because of the
somewhat convoluted semantics of the ao record which had to be preserved
To support conversion of record types to the new API, I added yet another
field to dbCommon, named PRST for 'PRocess STate'. This is a simple
DBF_UCHAR field. It is private to record support and serves as a
convenient place for record support to store it's processing state, so
that it knows how to proceed when being called again. See the process
routine of the converted aoRecord.
Anyway, not all is well in dbGetLinkCallback land.
The old dbGetLink is called from inside the iocCore. We are lucky that
this happens only in three places (and not seven;)
- in doBkpt (db/dbBkpt.c) and dbProcess (db/dbAccess.c) for SDIS
- in recGblGetTimeStamp (db/recGbl.c) for TSEL
The case for TSEL can be handled by adding another routine
recGblGetTimeStampCallback that calls dbGetlinkCallback instead of
The case for SDIS is not so easy. To be honest, I don't understand
anything about database breakpoints (never even used the feature). And
dbProcess itself is called in so many places that it didn't seem worth
the effort to make it asynchronous itself.
My current 'solution' is to ignore the problem. That is, SDIS is handled
slightly different than other input links, in that the APP flag is
converted to PP and a warning message issued that says so (see
dbPutString in dbStaticLib.c). [As a matter of choice, one could also
convert to NPP instead, i.e. ignore APP completely.]
Another problem is that converting a record support to use the new
dbGetLinkCallback (and also dbGetTimeStampCallback) means a major
re-write of the process routine. I think this is unavoidable. One could
argue that the record support code for most of the record types in base
is long due for a major overhaul anyway; and that the new way to write
process (as exemplified for the ao record) is clearer and more explicit
about the (due to device supports already inherent) asynchronicity. That
may all be fine and fair, but it still /is/ a major effort, especially if
full backward compatibility is desired.
Note: there is no pressure to convert record supports. They can stay the
same and work exactly as before. They will just ignore APP flags for
their input links (and treat them like NPP instead).
- Re: (A)synchronous dbGetLink Benjamin Franksen
- Navigate by Date:
RE: XML is dead, long live ML9 Jeff Hill
Re: XML is dead, long live ML9 Kay-Uwe Kasemir
- Navigate by Thread:
Re: Erlang Benjamin Franksen
Re: (A)synchronous dbGetLink Benjamin Franksen