From: Louis Z. <lou...@li...> - 2003-04-08 03:39:19
|
Dear James, Observer pattern is great for event dispatching. But I still have several concerns about that. I'm just mouthpiece of devil ;-) 1. In my view, there is only one 'event sink' per 'event source'. Open HPI impl. is the 'event sink' and plugins are 'event sources'. It is 1:* relationship. If my assumption is correct, observer is overkill than pull method, for example, following simple get_event function. 2. Who will trigger these callbacks? There are two methods. One is that 'event source' own a thread and spin. The other is that 'event sink' call "update()" function of 'event source' to have 'event source' found event and dispatched them. The former makes plugin call callback in the other thread and introduce a bundle of mutex/sync problems. Latter seems a little redundance -- if 'event sink' lets 'event source' find and dispatch event by callback, why does 'event source' not return events directly with the "update()" function? Remind, there is only one 'event sink' to recieve events. 3. SA HPI employes a sync event model. No callback at all. If we export a sync event API (GetEvent(...)) and employ async event model in impl., we complicate the Open HPI implementation. However, one argument in IRC is async/callback model will simplify plugin programming. For OpenIPMI, it is true because OpenIPMI library is callback-orientation programming. But I don't think it will still be true in HPI RPC plugin or other plugins. Does someone have experiences on other manangement platforms? Another argument on IRC is sync/poll model is slow than callback. It depends. Because app only gets event by "GetEvent()", callback in realtime does not make app faster. Open HPI impl. gets a changce to check all plugins for events, So I think sync/poll model is good enough... Does someone have more realtime requirements? So I'd like to propose another sync event model, I think it is correct if following three assumption is correct: 1. relationship between 'event sink' and 'event source' is 1:*, Open HPI impl. is always 'event sink' and plugins are always 'event source'. Plugin will never be a 'event sink' 2. All data should be stored in local hardware or remote manged node. Plugin should be only a translator between OpenHPI impl and lower local hardware or remote managed node. In most time, no caching. In fact, chching is harmful bacause the chched data might be out-of-date. 3. According to HPI RPC plugin call trace a) app --> b) openhpi lib --> c) hpi RPC plugin [node 0]------>[node 1] d) openhpid --> e) openhp lib --> f) lm_sensor plugin The step b->c is the inverse of the step c->e in information. It imply plugin interface should be equivalent to HPI API, with reducing redundance API of course. After all, I worked out a simple plugin interface. It just a start and definitely is a can of worms. In the interface, I really dislike mixing SA HPI definition with standard POSIX definition but re-declaring all SA Data Structure seems not a wisdom work... Any comments? -------------------------------------------------------------------------------- #ifndef __OPENHPI_H #define __OPENHPI_H #include <sys/time.h> #include <SaHpi.h> enum oh_event_type { OH_ET_SENSOR, OH_ET_HOTSWAP, OH_ET_WATCHDOG, OH_ET_OEM, OH_ET_USER, OH_ET_ENTITY, OH_ET_EVENT_LOG }; struct oh_entity_event { SaHpiEventCategoryT category; SaHpiEventStateT state; }; struct oh_sel_event { SaHpiEventCategoryT category; SaHpiEventStateT state; }; union oh_event_data_union { SaHpiSensorEventT sensor; SaHpiHotSwapEventT hotswap; SaHpiWatchdogEventT watchdog; SaHpiOemEventT oem; SaHpiUserEventT user; struct oh_entity_event entity; struct oh_sel_event sel; }; struct oh_event { enum oh_event_type type; union oh_event_data_union u; struct timeval timestamp; SaHpiSeverityT severity; }; enum oh_id_type { OH_ID_EVENT_LOG, OH_ID_WATCHDOG, OH_ID_INVENTORY, OH_ID_ENTITY, OH_ID_SENSOR, OH_ID_CONTROL, }; struct oh_id { enum oh_id_type type; unsigned char id[32]; }; struct oh_abi_v1 { /** * The function create an instance * @return the handler of the instance * @param name the mechanism's name. * for example, "snmp" for SNMP, "smi" for IPMI SMI * @param addr the interface name. * for example, "ipaddr:port" for SNMP, "if_num" for IPMI SMI */ void *(*open)(const char *name, const char *addr); /** * The function wait for event. * * * @remark at the start-up, plugins must send out ADD event for all * resources and items so as to OpenHPI can build up RPT/RDR. * @return 1 if an event is returned; 0 if timeout; otherwise an error * occur. * @param event if existing, plugin store the event. * @param timeout is an upper bound on the amount of time elapsed * before returns. It may be zero, causing select to return * immediately. * @param id if the event is return, plugin should store the * corresponding id. */ int (*get_event)(void *hnd, struct oh_event *event, struct timeval *timeout, struct oh_id *id); /** * get the id which the caller is running */ int (*get_sel_id)(void *hnd, struct oh_id *id); /** * get info from system event log */ int (*get_sel_info)(void *hnd, struct oh_id *id, SaHpiSelInfoT *info); /** * set time to system event log */ int (*set_sel_time)(void *hnd, struct oh_id *id, struct timeval *time); /** * set state to system event log */ int (*set_sel_state)(void *hnd, struct oh_id *id, int enabled); /** * get sensor info */ int (*get_sensor_info)(void *hnd, struct oh_id *id, SaHpiSensorDataFormatT *format); /** * get sensor data */ int (*get_sensor_data)(void *hnd, struct oh_id *id, SaHpiSensorReadingT *data); /** * get sensor thresholds */ int (*get_sensor_thresholds)(void *hnd, struct oh_id *id, SaHpiSensorThresholdsT *thres); /** * set sensor thresholds */ int (*set_sensoor_thresholds)(void *hnd, struct oh_id *id, const SaHpiSensorThresholdsT *thres); /** * get control info */ int (*get_control_info)(void *hnd, struct oh_id *id, SaHpiCtrlTypeT *type); /** * get control state */ int (*get_control_state)(void *hnd, struct oh_id *id, SaHpiCtrlStateT *state); /** * set control state */ int (*set_control_state)(void *hnd, struct oh_id *id, SaHpiCtrlStateT *state); /** * set inventory state */ int (*get_inventory_size)(void *hnd, struct oh_id *id, size_t *size); /** * get inventory state */ int (*get_inventory_info)(void *hnd, struct oh_id *id, SaHpiInventoryDataT *data); /** * set inventory state */ int (*set_inventory_info)(void *hnd, struct oh_id *id, SaHpiInventoryDataT *data); /** * get watchdog timer info */ int (*get_wwatchdog_info)(void *hnd, struct oh_id *id, SaHpiWatchdogT *wdt); /** * set watchdog timer info */ int (*set_wwatchdog_info)(void *hnd, struct oh_id *id, SaHpiWatchdogT *wdt); /** * reset watchdog timer info */ int (*reset_watchdog)(void *hnd, struct oh_id *id); /** * get hotswap state */ int (*get_hotswap_state)(void *hnd, struct oh_id *id, SaHpiHsStateT *state); /** * set hotswap state */ int (*set_hotswap_state)(void *hnd, struct oh_id *id, SaHpiHsStateT state); /** * request hotswap state */ int (*request_hotswap_action)(void *hnd, struct oh_id *id, SaHpiHsActionT act); /** * get power state */ int (*get_power_state)(void *hnd, struct oh_id *id, SaHpiHsPowerStateT *state); /** * set power state */ int (*set_power_state)(void *hnd, struct oh_id *id, SaHpiHsPowerStateT state); /** * get indicator state */ int (*get_indicator_state)(void *hnd, struct oh_id *id, SaHpiHsIndicatorStateT *state); /** * set indicator state */ int (*set_indicator_state)(void *hnd, struct oh_id *id, SaHpiHsIndicatorStateT state); /** * control parameter */ int (*control_parm)(void *hnd, struct oh_id *id, SaHpiParmActionT act); /** * get reset state */ int (*get_reset_state)(void *hnd, struct oh_id *id, SaHpiResetActionT *act); /** * set_reset state */ int (*set_reset_state)(void *hnd, struct oh_id *id, SaHpiResetActionT act); }; #endif/*__OPENHPI_H*/ On Tue, 2003-04-08 at 00:03, James Ketrenos wrote: > As I briefly went over on IRC the other day, the terminology is as follows: > > sink - the receiver of an event > source - the sender of an event > > There are two aspects to async events. The first is how you actually > connect the source/sink. The second is how you enable the asynchronous > nature of the event source. I'll address the first part here. > > The object that implements the sink is responsible for requesting that > the object implementing the source event register the sink as a callback. > > Some disclaimer on what follows... I'm using a pseudo IDL just for ease > of conversation--I'm not saying we need to use an IDL for the plugins. > > That said, supporting async events requires there to be an interface on > the source object through which the sink can register (in pseudo IDL): > > interface event { > int event_fn1(int arg1, char arg2, ...); > }; > interface event_source { > int register_sink(event* pevent); > }; > > The declaration of the event and the registration interface is done by > the source of the event. So, the event sink needs to do the following: > > 1. Get instantiated > 2. Instantiate the event source object > 3. Get the event source interface from the object instantiated in #2 > 4. Register itself as a callback point > > Since you may want to support multiple event registrations from the same > source to the same sink, as well as to simplify sink management on the > source side, we introduce a 'connection cookie' that can be used to > unregister a callback. So, event_source changes to: > > interface event_source { > int register_sink(event* pevent, unsigned long* pcookie); > int unregister_sink(unsigned long cookie); > }; > > We now have an issue with startup and tear down ordering--which I'll > discuss in another email (since its a different topic). > > As you can see, aside from the actual event structure type passed as the > first parameter in the register_sink method, that interface would be the > same regardless of which event is being requested. So, let's extend it > one more time: > > int register_sink(guid* interface_id, void* pevent, unsigned long* pcookie); > > Now we can define this as a "standard openhpi interface" and assign a > GUID to the event_source interface and when a source needs wants to hook > up with a particular object it would ask for the standard event > registration interface, request to register a call back to a known > interface, and wha-la. > > There are two benefits to this final extension: > > 1. Once defined, its a known pattern that all plugins can use for > setting up event bindings. This makes copy from example coding easier. > 2. It allows an object to be able to support new sink callback > interfaces without changing the interface contract to the outside world. > If an event supports being the source of an event, anyone can simply > ask that interface to add a callback to ANY sink type interface. If the > object doesn't support callback to that particular event type, it simply > returns an interface not supported error code. > > I'll cover the 'enabling asynchronous nature' in another mail. Right > now I have to get to a meeting. > > James |