In a multitasking environment, threads may need to know of condition changes that might affect them. In most cases where shared resources or multiple threads of execution exist, processes and objects must be sure of the integrity of data that they depend on and must be sent notice when that data changes.
In GEOS, this functionality is provided through the General Change Notification (GCN) mechanism. Although one could set up messages between processes and objects manually, the GCN mechanism eliminates the need to keep track of all processes that depend on the particular change and to keep track of all messages sent out to the various processes and objects.
1 Design Goals
2 The Mechanics of GCN
3 System Notification
3.1 Registering for System Notification
3.2 Handling System Notification
3.3 Removal from a System List
4 Application Local GCN Lists
4.1 Creating Types and Lists
4.2 Registering for Notification
4.3 Handling Application Notification
4.4 Removal from Application Lists
General Change Notification allows you to keep track of both system and application events. Objects or processes interested in a particular change may request the GCN mechanism to notify them when that change occurs. That change may be system-wide (such as a file system change) or application-specific (such as a text style change within a word processor). The GCN mechanism allows objects or processes to sign up for such notification and intercept messages sent by the system (or the application) so that you may respond to different changes on a case by case basis.
The manners in which you sign up for these two types of notification differ although the functionality of the notification process is similar. The most straightforward way is to use a
gcnList
field in an application's application object; this is the usual approach used to add an application's GenPrimary object to the application's window list. We will assume that you know how to add an object to a GCN list in this manner, as it is shown in most if not all of the sample applications.
You can also sign up for system-wide notification through the use of certain routines and intercept system messages when the change occurs. Other objects may sign up for application-specific notification supported by
GenApplicationClass
. These application specific notifications should only be sent to the GenApplication object.
The basic GCN functionality manages lists of objects that are interested in specific changes. For each particular change that needs to be monitored, a separate list is needed. A completely separate "list of lists" containing an inventory of all GCN lists is also created. This will serve as the "search" table, while the particular GCN list will serve as the "messaging" list. When an event is detected, the GCN mechanism will search through the list of lists, seeing if a notification list is interested in a particular change, and send the appropriate messages if the objects do indeed wish notification of the event.
There are several reasons why you would want to use GCN:
GCNListAdd(), GCNListSend()
The system provides several lists for common system changes which might affect your application. After signing up on one of these lists (for example, the file change list) you will be notified by the kernel whenever the specified change occurs. In most cases, all you need to do is register for notification with
GCNListAdd()
and intercept the kernel's notification message.
The GCN mechanism performs its functions through a common series of steps. These steps are:
GCNListAdd().
GCNListSend()
.Registering for System Notification
Whenever an object or process needs to be notified of some system change, it must call the routine
GCNListAdd()
to add itself to the list for that particular change.
GCNListAdd
() finds the appropriate general change notification list--creating a new one if none currently exists--and adds the optr of the new object to the end of that list. You may add the optr to the GCN list at any time during the process' or object's life, but it is usually convenient for a process to be added in its
MSG_GEN_PROCESS_OPEN_APPLICATION
or for an object that is on the active list to be added in its
MSG_META_ATTACH
handler.
Each optr in a GCN list should have a notification ID attached to it. (The combination of a manufacturer ID and a notification type comprises an element's specific notification ID.)
GCNListAdd()
must be passed the optr of the object to add, along with a notification ID. For each separate notification ID, a separate GCN list is needed and will be created automatically.
The GCN routines use a word of data,
GCNStandardListType
, to represent the notification type. The currently recognized
GCNStandardListType
types for MANUFACTURER_ID_GEOWORKS are
GCNStandardListType
is used for notification of a file system change. Parties on this list will receive the system messages
MSG_NOTIFY_FILE_CHANGE
and
MSG_NOTIFY_DRIVE_CHANGE
.
GCNStandardListType
is used for notification of a starting or exiting application. Parties on this list will receive the system messages MSG_NOTIFY_APP_STARTED and
MSG_NOTIFY_APP_EXITED
.
GCNStandardListType
is used for notification of a date/time change in the system's internal clock.
Note that this will not tell you about timer ticks--the only time changes that will come up are those resulting from system restarts and time changes by the user. Parties on this list will receive the system message
MSG_NOTIFY_DATE_TIME_CHANGE.
This message does not pass any further information, so your message handler should be able to take care of any changes by itself (such as calling the internal clock for an updated value).
GCNStandardListType
is used for notification of a user dictionary change. Parties on this list will receive the system message
MSG_NOTIFY_USER_DICT_CHANGE.
GCNStandardListType
notifies various system utilities that an express menu has been created or destroyed. The recipient receives the optr of the Express Menu Control. This list should be used in conjunction with the GCNSLT_EXPRESS_MENU_OBJECTS list. Objects on this list receive MSG_NOTIFY_EXPRESS_MENU_CHANGE, which itself passes a
GCNExpressMenuNotificationType
(either GCNEMNT_CREATED or GCNEMNT_DESTROYED) and the optr of the Express Menu Control affected.
SpoolGetNumPrinters()
to learn if any printer or fax drivers are currently installed.
GCNStandardListType
is used for system shutdown control.
Parties on a list of this type will receive the system message MSG_META_CONFIRM_SHUTDOWN which itself passes a
GCNShutdownControlType
(either GCNSCT_SUSPEND, GCNSCT_SHUTDOWN, or GCNSCT_UNSUSPEND). Shutdown Control is documented in the Applications and Geodes chapter.
GCNListSend()
.These pre-defined notification types are intended only for use with MANUFACTURER_ID_GEOWORKS. Other manufacturers wishing to intercept their own system changes must define their own change types under their respective manufacturer IDs if they are unable to use MANUFACTURER_ID_GEOWORKS.
Code Display 9-1 Adding a Process Object to a GCN List
@method MyProcessClass, MSG_GEN_PROCESS_OPEN_APPLICATION {
optr myThread;
@callsuper; /* Do default MSG_GEN_PROCESS_OPEN_APPLICATION */
/* Casts the return value for the process handle into an optr */
myThread = ConstructOptr(GeodeGetProcessHandle(), NullChunk);
/* myThread (the process) is added to notification of file changes */
GCNListAdd(myThread, MANUFACTURER_ID_GEOWORKS, GCNSLT_FILE_SYSTEM);
}
MSG_NOTIFY_FILE_CHANGE, MSG_NOTIFY_DRIVE_CHANGE, MSG_NOTIFY_APP_STARTED, MSG_NOTIFY_APP_EXITED, MSG_NOTIFY_USER_DICT_CHANGE, MSG_NOTIFY_EXPRESS_MENU_CHANGE
When an identified change occurs, either the system (or a library) will call the routine
GCNListSend()
, passing it the appropriate notification message. This routine scans the list of all GCN lists and dispatches notification to all appropriate objects that had requested knowledge of the specified change. If any additional information relating to the change cannot be included in the message, the system allows
GCNListSend()
to pass data in a global heap block. For example, additional information about a file change (name of file, etc.) must be passed in a global heap block.
The object or process originally requesting notification of the change should be required to handle the appropriate message. If additional data about the change is passed in a global heap block, the process should access that information with
MemLock()
and
MemUnlock()
. You should always call the process's superclass in your message handler to make sure that the global heap block will be automatically freed by
MetaClass
. Therefore, do not free a global heap block manually in a notification handler.
The system provides several messages which you may want to handle. These messages provide notification of file system changes, application start-up or shut-down, system clock changes, etc. These messages are mentioned with the list they correspond to in Registering for System Notification . Messages which require more detailed explanation are also mentioned below.
The kernel sends
MSG_NOTIFY_FILE_CHANGE
whenever a change to the file system occurs. All objects signed up on the GCN list GCNSLT_FILE_CHANGE will receive this message.
MSG_NOTIFY_FILE_CHANGE
passes a
FileChangeNotificationType
specifying the change that occurred. Some types indicate the presence of a data block (of type
FileChangeNotificationData)
containing, if applicable, the name of the file changed, the disk handle of the file changed, and the ID of the affected file.
The notification type should be one of the following:
id
stores the ID of the containing directory; FCND_
name
contains the name of the new file or directory that was created.
id
stores the ID of the file or directory that was renamed; FCND_
name
contains its new name.
id
stores the ID of the file. FCND_
name
is undefined, and may or may not be present. (You can check the size of the block to see if it is indeed present.) This notification type is generated after a call to
FileEnableOpenCloseNotification()
.
id
stores the ID of the file or directory that was deleted. FCND_
name
is undefined and may or may not be present.
id
stores the ID of the file. FCND_
name
is undefined and may or may not be present. This notification type is generated after a call to
FileCommit()
or
FileClose()
that results in a file modification.
id
stores the ID of the file. FCND_
name
is undefined and may or may not be present. This notification type is generated upon completion of all changes in a
FileSetAttributes()
,
FileSetHandleExtAttributes()
, or
FileSetPathExtAttributes()
call.
id
and FCND_
name
are undefined and may not be present.
_id
stores the identifier of the file. FCND
_name
is undefined and may not be present. This notification type is generated only after a call to
FileEnableOpenCloseNotification()
.
FileChangeBatchNotificationData
block instead. This data block stores a batch of
FileChangeBatchNotificationItem
structures, each referring to an operation (with its own notification type, disk handle, file name, and file ID). Note that in this batched case, you must assume that all file names and file IDs that are optional (i.e. are undefined) are not present.
StandardPath
. FCND_
disk
contains the
StandardPath
that was added. This notification type is generated after a call to
FileAddStandardPathDirectory()
.
StandardPath
. FCND_
disk
contains the
StandardPath
that was deleted. This notification type is generated after a call to
FileDeleteStandardPathDirectory()
.You may access this data (after locking the block) and perform whatever actions you need within your message handler.
The kernel also sends MSG_NOTIFY_DRIVE_CHANGE to GCN lists of type GCNSLT_FILE_CHANGE. This message passes a
GCNDriveChangeNotificationType
specifying whether a drive is being created or destroyed and the ID of the affected drive.
The kernel sends
MSG_NOTIFY_APP_STARTED
whenever any application starts up within the system and
MSG_NOTIFY_APP_EXITED
whenever an application shuts down. All objects signed up on the GCN list GCNSLT_APPLICATION will receive these messages after the change occurs.
MSG_NOTIFY_APP_STARTED
passes the MemHandle of the application starting up, which you may access to perform any required actions. In a similar manner,
MSG_NOTIFY_APP_EXITED
passes the MemHandle of the application shutting down.
The kernel sends
MSG_NOTIFY_USER_DICT_CHANGE
whenever the system changes the current user dictionary in use. All objects signed up for the GCN list GCNSLT_USER_DICT_CHANGE will receive this message after the change occurs.
MSG_NOTIFY_USER_DICT_CHANGED
passes the MemHandle of the Spell Box causing the change and the MemHandle of the user dictionary being changed, both of which you may access in your message handler.
You should use
GCNListRemove()
to remove an object from a system GCN list. You must pass the notification ID (
GCNStandardListType
and Manufacturer ID) and the optr of the object to be removed. The optr of the object in question will only be removed from the list of the particular change specified. If the optr is on several GCN lists, those other lists will remain unchanged.
An object or process in the course of dying must remove itself from all GCN lists that it is currently on. You should therefore keep track of all GCN lists you add a particular object to. It is usually convenient for a process to remove itself from these lists within its
MSG_GEN_PROCESS_CLOSE_APPLICATION
message handler or for an object to remove itself in its
MSG_META_DETACH
handler.
Code Display 9-2 Removing a Process from a GCN list
@method MyProcessClass, MSG_GEN_PROCESS_CLOSE_APPLICATION {
optr myThread;
myThread = ConstructOptr(GeodeGetProcessHandle(), NullChunk);
GCNListRemove(myThread, MANUFACTURER_ID_GEOWORKS, GCNSLT_FILE_CHANGE);
@callsuper;
}
The GCN mechanism not only allows you to keep track of system changes but also allows you to keep track of changes within a specific application. These application-specific GCN lists operate in slightly different manners than the system-wide application lists. There are an extensive number of pre-defined application lists for MANUFACTURER_ID_GEOWORKS. You may use these if you like, but in most cases you will want to create your own list and notification types for your application.
The GenControl objects make extensive use of these types of GCN lists when implementing changes. For a complete discussion of using these lists within the context of a GenControl, see the Controllers chapter.
If you will be creating custom GenControl objects or just wish to set up a notification system within your application, you will want to create your own application GCN lists when using this mechanism. To do this, you must follow a few preliminary steps:
GenAppGCNListTypes
within an appropriate
yourCompanyName.h
file.
NotificationTypes
for the above list type within the
yourCompanyName.h
file.The GCN mechanism in this case performs its functions through a common series of steps. These steps are similar to the steps needed for system-wide notification:
MSG_META_GCN_LIST_ADD
.
MSG_META_NOTIFY
or
MSG_META_NOTIFY_WITH_DATA_BLOCK
, the notification list type to use, and the data block to pass (if applicable).
MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST
to pass the event. You may have to pass some
GCNListSendFlags
with this message. This message acts as a dispatch routine, sending all interested parties the recorded event
MSG_META_NOTIFY
.
MSG_META_NOTIFY
or
MSG_META_NOTIFY_WITH_DATA_BLOCK
. If you need to perform some work related to this change, you should have a message handler to intercept these messages.Handling Application Notification
Removal from Application Lists
It is a relatively simple matter to create your own notification types. Within an appropriate company-specific file merely create your own types and lists. (For example, all Geoworks application-local lists are within the file geoworks.h .)
Code Display 9-3 Creating New Notification Types and Lists
/* These types should be placed within an appropriate yourCompanyName.h file. */
/* First create a group of Notification types to use for your MANUFACTURER_ID. */
typedef enum {
<yourCompanyName>_NT_CUSTOM_NOTIFICATION_NUMBER_ONE
<yourCompanyName>_NT_CUSTOM_NOTIFICATION_NUMBER_TWO
...
} <yourCompanyName>NotificationTypes;
/* Then create whatever Notification list types you need. These list types * usually correspond one-to-one to the types enumerated above. It is possible, * however, for several lists to be interested in a single notification type. */
typedef enum {
<yourCompanyName>_GAGCNLT_CUSTOM_LIST_TYPE_ONE
<yourCompanyName>_GAGCNLT_CUSTOM_LIST_TYPE_TWO
...
} <yourCompanyName>GenAppGCNListTypes;
MSG_META_GCN_LIST_ADD
Registering for application notification is simple once you have created your own custom notification lists. Whenever an object or process needs to be notified of an application change, you should call
MSG_META_GCN_LIST_ADD
to add that object or process to the list interested in that particular change.
MSG_META_GCN_LIST_ADD
finds the appropriate custom GCN list and adds the optr of the new object to the end of that list. (If no space for the list currently exists because it is empty, the message will allocate space for the list automatically.) You may add the interested optr at any time during the process' or object's life, but it is usually convenient for a process to be added in its
MSG_GEN_PROCESS_OPEN_APPLICATION
or for an object to be added in its
MSG_META_ATTACH
handler.
Each optr in a GCN list should have a notification ID attached to it. The combination of a manufacturer ID and a notification type comprises an element's specific notification ID.
MSG_META_GCN_LIST_ADD
must pass the optr of the object to add, along with a notification ID. For each separate notification ID, a separate GCN list is needed and will be created automatically.
Geoworks has several pre-defined GCN lists of type
GeoWorksGenAppGCNListType
for use by applications. You will probably have only limited use for these; these list types are used mostly by the UI controllers. For information on these types and how the various classes use them, see the Controllers chapter.
Code Display 9-4 Adding Yourself to a Custom GCN List
@method MyProcessClass, MSG_GEN_PROCESS_OPEN_APPLICATION {
@callsuper; /* Do default MSG_GEN_PROCESS_OPEN_APPLICATION */
myThread = ConstructOptr(GeodeGetProcessHandle(), NullChunk);
/* myThread (the process) is added to notification of TYPE_ONE changes */
@call MyApplication::MSG_META_GCN_LIST_ADD(myThread,
yourCompanyName_GAGCNLT_CUSTOM_LIST_TYPE_ONE,
MANUFACTURER_ID_yourCompanyName);
}
MSG_META_NOTIFY, MSG_META_NOTIFY_WITH_DATA_BLOCK, MSG_META_GCN_LIST_SEND
When a change occurs in the application that needs to send out notification, you must set up the notification message and send it to the interested list. You may attach a data block to this notification for use by the objects on the notification list. To send out these notifications, you should use
MSG_META_NOTIFY
or
MSG_META_NOTIFY_WITH_DATA_BLOCK
(when passing data).
In the simplest case without the need to pass data, you should encapsulate
MSG_META_NOTIFY
with the particular Notification ID (notification type and Manufacturer ID) that should be notified. You should then send
MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST
to your application object with this event and the particular GCN list interested in this change. (Note that you will have to keep track of which lists are interested in which notification types.) Make sure that you perform a send (not a call) when using this message as the message may cross threads.
Code Display 9-5 Using MSG_META_NOTIFY
@method MyProcessClass, MSG_SEND_CUSTOM_NOTIFICATION {
MessageHandle event;
/* First encapsulate the MSG_META_NOTIFY with the type of list and manufacturer ID * interested in the change. Since this message is being recorded for no class in * particular, use NullClass.*/
event = @record (optr) NullClass::MSG_META_NOTIFY( MANUFACTURER_ID_yourCompanyName, yourCompanyName_NT_CUSTOM_TYPE_ONE);
/* Then send this MSG_META_NOTIFY using MSG_META_GCN_LIST_SEND. You must make sure * to pass the particular GCN list interested in the changes encapsulated in the * above message. */
@send MyProcess::MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST ( (word) 0, /* GCNListSendFlags */ event, /* Handle to MSG_NOTIFY event above. */ 0, /* No data passed, so no data block. */ /* Pass the list interested in NT_CUSTOM_TYPE_ONE notification types. */ yourCompanyName_GAGCNLT_APP_CUSTOM_LIST_ONE, /* Pass your manufacturer ID. */ MANUFACTURER_ID_yourCompanyName); }
If instead you need to pass a data block along with the notification, you should use
MSG_META_NOTIFY_WITH_DATA_BLOCK
. You should set up the structure to pass beforehand. You must also make sure to add a reference count to the data block equal to the number of
lists
(not objects) you wish to send the notification. To do this, call
MemInitRefCount()
with the data block and the total number of lists you are sending the notification to. (In most cases, you will only send notification to one list, although, of course, that list may have several objects.)
Code Display 9-6 MSG_META_NOTIFY_WITH_DATA_BLOCK
@method MyProcessClass, MSG_SEND_CUSTOM_NOTIFICATION {
typedef struct {
int number;
char letterToLookFor;
} MyDataStructure;
MemHandle myDataBlock;
MyDataStructure *myDataPtr;
MessageHandle event;
/* Allocate and lock down a block for the data structure. This will be passed * along with the notification. NOTE: data blocks must be sharable! */
myDataBlock = MemAlloc(sizeof(MyDataStructure), (HF_DYNAMIC | HF_SHARABLE), HAF_STANDARD);
myDataPtr = MemLock(myDataBlock);
/* Load up the structure with pertinent information. */
myDataPtr->count = 200;
myDataPtr->letterToLookFor = `z';
/* Unlock it and set its reference count to 1 as we are only sending this to one
* list. */
MemUnlock(myDataBlock);
MemInitRefCount(myDataBlock, (word) 1);
/* Now encapsulate a MSG_META_NOTIFY_WITH_DATA_BLOCK message. Since it is being * recorded for no particular class, use NullClass as its class type. */
event = @record (optr) NullClass::MSG_META_NOTIFY_WITH_DATA_BLOCK( MANUFACTURER_ID_yourCompanyName, /* Manufacturer ID */ NT_CUSTOM_TYPE_ONE, /* List type. */ myDataBlock); /* handle of data block */
/* Finally, send the message off to our process. The GCNListSendFlags depend on * the situation. */
@send MyProcess::MSG_GEN_PROCESS_SEND_TO_APP_GCN_LIST( (word) 0, /* GCNListSendFlags */ event, /* Handle to message */ myDataBlock, /* Handle of data block */ /* Pass the type of list interested in NT_CUSTOM_TYPE_ONE notification. */ GAGCNLT_APP_CUSTOM_LIST_ONE, MANUFACTURER_ID_yourCompanyName);
/* All done! myDataBlock will be MemFree()'d automatically. */ }
The object or process originally requesting notification of the change will want to provide a handler for the
MSG_META_NOTIFY
or
MSG_META_NOTIFY_WITH_DATA_BLOCK
. If additional data about the change is passed in a data block, the process should access that information with
MemLock()
and
MemUnlock()
. You should always call the process's superclass in your message handler, to make sure that the global heap block will be automatically freed by
MetaClass
. Therefore, do not free a notification data block manually in a notification handler.
Code Display 9-7 Intercepting an Application Notification Change
/* Code to implement when MyObjectClass receives MSG_META_NOTIFY with a certain * notification type. */
@method MyObjectClass, MSG_META_NOTIFY {
MyDataStructure myData; /* Stores the passed data block. */
/* Lock the data structure. */
myData = MemLock(data);
/* Check the notification type and implement the changes you wish to occur in * response to the previous event. */
if ((notificationType == yourCompanyName_NT_CUSTOM_TYPE_ONE) &
(manufID == MANUFACTURER_ID_yourCompanyName)){
/* Code to implement for your object. */
}
MemUnlock(data);
@callsuper; /* Important! Frees data block. */ }
You should use
MSG_META_GCN_LIST_REMOVE
to remove an object from an application GCN list. You must pass the routine the notification ID (
yourCompanyName
AppGCNListTypes
and Manufacturer ID) and the optr of the object to remove. Note that the optr of the object in question will only be removed from the list of the particular change specified. If the optr is on several GCN lists, those other lists will remain unchanged.
An object or process in the course of dying must remove itself from all GCN lists that it is currently on, both from the system and from an application. You should therefore keep track of all GCN lists you add a particular object to. It is usually convenient for a process to remove itself from these lists within its
MSG_GEN_PROCESS_CLOSE_APPLICATION
message handler or for an object to remove itself at
MSG_META_DETACH
time.
Code Display 9-8 Removing from an Application GCN List
@method MyProcessClass, MSG_GEN_PROCESS_CLOSE_APPLICATION {
@send MyApplication::MSG_META_GCN_LIST_REMOVE( MyObject, /* optr to remove from list. */ yourCompanyName_NT_CUSTOM_LIST_ONE, /* list to remove object from. */ /* Manufacturer ID of list to remove object from. */ MANUFACTURER_ID_yourCompanyName);
@callsuper; }