Here are my notes on the MIDAS ODB "hot link" function. Perhaps others can find them useful.
Using db_open_record(key,function), the user can tell MIDAS to call the specified user function when the specified ODB key is modified by any other MIDAS program. This function works both locally (shared memory odb access) and remotely (odb access through mserver tcp rpc). For example, the MIDAS "history" mechanism is implemented in the mlogger by "hot-linking" ODB "/equipment/xxx/Variables".
First, the relevant data structures defined in midas.h and msystem.h (ODB database headers, etc)
- define NAME_LENGTH 32 /**< length of names, mult.of 8! */
- define MAX_CLIENTS 64 /**< client processes per buf/db */
- define MAX_OPEN_RECORDS 256 /**< number of open DB records */
(in msystem.h) DATABASE buf <--- local, private to each client)
DATABASE_HEADER* database_header <--- odb in shared memory char name[NAME_LENGTH] DATABASE_CLIENT client[MAX_CLIENTS] char name[NAME_LENGTH] OPEN_RECORD open_record[MAX_OPEN_RECORDS] handle access_mode flags
(the above means that each midas client has access to the list of all open records through buf->database_header.client[i].open_record[j])
Second, the data path through db_set_data & co: (other odb "write" functions work the same way)
lock db update odb <--- memcpy(), really db_notify_clients(key) unlock db return
loop: <--- data for this key changed and so data for all keys containing it also changed, and we need to notify anybody who has an open record on the parents of this key. need to loop over parents of this key (follow "..") if (key->notify_count) foreach client foreach open_record if (open_record.handle == key) ss_resume(client->port, "O hDB hKey") key = key.parent goto loop;
idx = ss_suspend_get_index() <--- magic here send udp message ("O hDB hKey") to localhost:port <-- notifications sent only to local host!
note 1: I do not completely understand the ss_suspend_xxx() stuff. The best I can tell is it creates a number of udp sockets bound to the local host and at least one udp rpc receive socket ultimately connected to the cm_dispatch_rpc() function.
note 2: More magic here: database_header->client[i].port appears to be the udp rpc server port of the mserver, while ODB /Clients/xxx/Port is the tcp rpc server port of the client itself, on the remote host
note 3: the following is for remote odb clients connected through the mserver. For local clients, cm_dispatch_rpc() calls the local db_update_record() as shown at the very end.
note 4: this uses udp rpc. If the udp datagram is lost inside the os kernel (it looks like these udp/rpc datagrams never go out to the network), "hot-link" silently fails: code below is not executed. Some OSes (namely, Linux) are known to lose udp datagrams with high probability under certain not very well understood conditions.
local mserver receives the udp datagram
... cm_dispatch_ipc() if (message=="O hDB hKey") decode message (hDB, hKey) db_update_record(hDB, hKey) send tcp rpc with args(MSG_ODB, hDB, hKey)
(note- unlike udp rpc, tcp rpc are never "lost")
remote client receives tcp rpc: rpc_client_dispatch()
recv_tcp(net_buffer) if (net_buffer.routine_id == MSG_ODB) db_update_record(hDB, hKey)
if remote delivery, see cm_dispatch_ipc() above <--- local delivery foreach (_recordlist) if (recordlist.handle == hKey) if (!recordlist.access_mode&MODE_WRITE) db_get_record(hDB,hKey,recordlist.data,recordlist.size) recordlist.dispatcher(hDB,hKey,recordlist.info); <-- user-supplied handler
Note: the dispatcher() above is the function supplied by the user in db_open_record().