windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/h/om.h
2020-09-26 16:20:57 +08:00

6647 lines
199 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// Object Manager
//
#ifndef _H_OM
#define _H_OM
#include <gdc.h>
#include <ast120.h>
//
//
// CONSTANTS
//
//
//
// Function Profiles (apps)
//
#define OMFP_FIRST 0
typedef enum
{
OMFP_AL = OMFP_FIRST,
OMFP_OM,
OMFP_WB, // For old whiteboard
OMFP_MAX
}
OMFP;
//
// These are the corresponding strings (part of the protocol)
//
#define AL_FP_NAME "APP-LOAD-1.0"
#define OM_FP_NAME "OMCONTROL-1.0"
#define WB_FP_NAME "WHITEBOARD-1.0"
//
// ObMan clients
//
#define OMCLI_FIRST 0
typedef enum
{
OMCLI_AL = OMCLI_FIRST,
OMCLI_WB,
OMCLI_MAX
}
OMCLI;
//
// ObMan workset groups
//
#define OMWSG_FIRST 0
typedef enum
{
OMWSG_OM = OMWSG_FIRST,
OMWSG_AL,
OMWSG_WB,
OMWSG_MAX,
OMWSG_MAXPERCLIENT
}
OMWSG;
//
// These are the corresponding strings (part of the protocol)
//
#define OMC_WSG_NAME "OBMANCONTROL"
#define AL_WSG_NAME "APP-LOADER"
#define WB_WSG_NAME "WHITEBOARD"
//
// Specify this in place of a valid Domain handle to create/move a workset
// group outside of any calls:
//
#define OM_NO_CALL NET_INVALID_DOMAIN_ID
//
//
// SYSTEM LIMITS
//
// These are limits imposed by the architecture/design of the system.
//
//
//
// Workset group names
//
// Workset groups names are null-terminated strings, up to 32 characters
// long (including the NULL character). They are intended to be
// human-readable names, and must contain only ASCII characters between
// 0x2C and 0x5B. This range includes all uppercase characters, all digits
// and certain punctuation marks.
//
#define OM_MAX_WSGROUP_NAME_LEN 32
//
// Function Profile names
//
// Function profile names must be no longer than 16 characters (including
// the NULL character). The range of characters allowable is the same as
// for workset group names.
//
#define OM_MAX_FP_NAME_LEN 16
//
// Maximum number of changes allowed to a workset
//
// Each time a workset is changed, we increment its "generation number",
// which is used in resequencing operations. The largest size for an
// integer is 32 bits, so the maximum generation number using a convenient
// type is 2^32-1.
//
//
#define OM_MAX_GENERATION_NUMBER 0xffffffff
//
// Maximum size of an object
//
// This derives from the maximum size of a huge memory block under Windows
// (16MB less 64KB):
//
#define OM_MAX_OBJECT_SIZE ((UINT) (0x1000000 - 0x10000))
//
// Maximum update size of an object
//
// This derives from the necessity to send updates atomically in one
// network packet (see SFR 990)
//
#define OM_MAX_UPDATE_SIZE ((UINT) (0x00001f00))
//
// Maximum number of worksets per workset group
//
// This derives from the desire to make workset IDs 8-bit quantities so
// that they can fit in DC-Groupware events with a workset group handle and
// an object ID:
//
// Note: this value must be at most 255 so that certain ObMan for-loops
// don't cycle for ever.
//
#define OM_MAX_WORKSETS_PER_WSGROUP 255
//
// Maximum number of workset groups per Domain
//
// This derives from the use of the ObManControl workset group: it has one
// control workset and then one workset for each workset group in the
// Domain, so there can only be this many workset groups in a Domain:
//
// Note: this number must be at most one less that
// OM_MAX_WORKSETS_PER_WSGROUP
//
#define OM_MAX_WSGROUPS_PER_DOMAIN 64
//
// Special WSGROUPID for OMC:
//
#define WSGROUPID_OMC 0
//
//
// RETURN CODES
//
// Return codes are defined relative to the OM_BASE_RC base
//
//
enum
{
OM_RC_NO_MORE_HANDLES = OM_BASE_RC,
OM_RC_WORKSET_DOESNT_EXIST,
OM_RC_WORKSET_EXHAUSTED,
OM_RC_OBJECT_DELETED,
OM_RC_BAD_OBJECT_ID,
OM_RC_NO_SUCH_OBJECT,
OM_RC_WORKSET_LOCKED,
OM_RC_TOO_MANY_CLIENTS,
OM_RC_TOO_MANY_WSGROUPS,
OM_RC_ALREADY_REGISTERED ,
OM_RC_CANNOT_MOVE_WSGROUP,
OM_RC_LOCAL_WSGROUP,
OM_RC_ALREADY_IN_CALL,
OM_RC_NOT_ATTACHED,
OM_RC_WORKSET_ALREADY_OPEN,
OM_RC_OUT_OF_RESOURCES,
OM_RC_NETWORK_ERROR,
OM_RC_TIMED_OUT,
OM_RC_NO_PRIMARY,
OM_RC_WSGROUP_NOT_FOUND,
OM_RC_WORKSET_NOT_FOUND,
OM_RC_OBJECT_NOT_FOUND,
OM_RC_WORKSET_LOCK_GRANTED,
OM_RC_SPOILED,
OM_RC_RECEIVE_CB_NOT_FOUND,
OM_RC_OBJECT_PENDING_DELETE,
OM_RC_NO_NODES_READY,
OM_RC_BOUNCED
};
//
// Setting defaults
//
#define OM_LOCK_RETRY_COUNT_DFLT 10
#define OM_LOCK_RETRY_DELAY_DFLT 1000
#define OM_REGISTER_RETRY_COUNT_DFLT 40
#define OM_REGISTER_RETRY_DELAY_DFLT 5000
//
// This is the number of bytes we zero at the start of each object
// allocated. It must be less than DCMEM_MAX_SIZE, since ObjectAlloc
// assumes that this many bytes at the start of an object are all in the
// same segment.
//
#define OM_ZERO_OBJECT_SIZE 0x400
//
// EVENTS
// Public then Internal
//
enum
{
OM_OUT_OF_RESOURCES_IND = OM_BASE_EVENT,
OM_WSGROUP_REGISTER_CON,
OM_WSGROUP_MOVE_CON,
OM_WSGROUP_MOVE_IND,
OM_WORKSET_OPEN_CON,
OM_WORKSET_NEW_IND,
OM_WORKSET_LOCK_CON,
OM_WORKSET_UNLOCK_IND,
OM_WORKSET_CLEAR_IND,
OM_WORKSET_CLEARED_IND,
OM_OBJECT_ADD_IND,
OM_OBJECT_MOVE_IND,
OM_OBJECT_DELETE_IND,
OM_OBJECT_REPLACE_IND,
OM_OBJECT_UPDATE_IND,
OM_OBJECT_LOCK_CON,
OM_OBJECT_UNLOCK_IND,
OM_OBJECT_DELETED_IND,
OM_OBJECT_REPLACED_IND,
OM_OBJECT_UPDATED_IND,
OM_PERSON_JOINED_IND,
OM_PERSON_LEFT_IND,
OM_PERSON_DATA_CHANGED_IND,
OMINT_EVENT_LOCK_TIMEOUT,
OMINT_EVENT_SEND_QUEUE,
OMINT_EVENT_PROCESS_MESSAGE,
OMINT_EVENT_WSGROUP_REGISTER,
OMINT_EVENT_WSGROUP_MOVE,
OMINT_EVENT_WSGROUP_REGISTER_CONT,
OMINT_EVENT_WSGROUP_DEREGISTER,
OMINT_EVENT_WSGROUP_DISCARD
};
//
// Data transmission constants:
//
#define OM_NET_MAX_TRANSFER_SIZE 60000
//
// These constants identify the types of network buffer pools we use:
//
#define OM_NET_OWN_RECEIVE_POOL 1
#define OM_NET_OMC_RECEIVE_POOL 2
#define OM_NET_WSG_RECEIVE_POOL 3
#define OM_NET_SEND_POOL 4
//
// These constants are the sizes of the receive pools for each priority and
// for each type of channel we join:
//
#define OM_NET_RECEIVE_POOL_SIZE 0x00002000
#define OM_NET_OWN_RECEIVE_POOL_TOP OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_OWN_RECEIVE_POOL_HIGH OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_OWN_RECEIVE_POOL_MEDIUM OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_OWN_RECEIVE_POOL_LOW OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_OMC_RECEIVE_POOL_TOP OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_OMC_RECEIVE_POOL_HIGH OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_OMC_RECEIVE_POOL_MEDIUM OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_OMC_RECEIVE_POOL_LOW OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_WSG_RECEIVE_POOL_TOP OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_WSG_RECEIVE_POOL_HIGH OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_WSG_RECEIVE_POOL_MEDIUM OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_WSG_RECEIVE_POOL_LOW OM_NET_RECEIVE_POOL_SIZE
#define OM_NET_SEND_POOL_SIZE 0x00004000
#define OM_NET_SEND_POOL_TOP OM_NET_SEND_POOL_SIZE
#define OM_NET_SEND_POOL_HIGH OM_NET_SEND_POOL_SIZE
#define OM_NET_SEND_POOL_MEDIUM OM_NET_SEND_POOL_SIZE
#define OM_NET_SEND_POOL_LOW OM_NET_SEND_POOL_SIZE
//
// These constants are used to decide what priority to send data transfers
// at when a Client has specified OBMAN_CHOOSES_PRIORITY for the workset:
//
#define OM_NET_HIGH_PRI_THRESHOLD 0x0100
#define OM_NET_MED_PRI_THRESHOLD 0x1000
#define OM_CHECKPOINT_WORKSET OM_MAX_WORKSETS_PER_WSGROUP
//
//
// DATA STRUCTURES
//
// This section defines the main data structures of the ObMan API.
//
//
typedef struct tagOM_CLIENT * POM_CLIENT;
typedef struct tagOM_PRIMARY * POM_PRIMARY;
// Client objects are record pointers
typedef struct tagOM_OBJECT * POM_OBJECT;
typedef struct tagOM_WSGROUP * POM_WSGROUP;
typedef struct tagOM_DOMAIN * POM_DOMAIN;
//
// ObMan correlators
//
typedef WORD OM_CORRELATOR;
//
// Workset ID
//
// Within a workset group, worksets are identified by an 8-bit ID.
//
typedef BYTE OM_WSGROUP_HANDLE;
typedef BYTE OM_WORKSET_ID;
typedef OM_WORKSET_ID * POM_WORKSET_ID;
//
// Object structure
//
// Objects and object pointers are defined as follows:
//
typedef struct tagOM_OBJECTDATA
{
TSHR_UINT32 length; // length of the data field
BYTE data[1]; // object data, uninterpreted by ObMan;
// in reality, not 1 byte but <length>
// bytes long
}
OM_OBJECTDATA;
typedef OM_OBJECTDATA * POM_OBJECTDATA;
typedef POM_OBJECTDATA * PPOM_OBJECTDATA;
//
// Note that the maximum permitted size of an object, INCLUDING the
// <length> field, is 16MB less 64KB.
//
void __inline ValidateObjectData(POM_OBJECTDATA pData)
{
ASSERT(!IsBadWritePtr(pData, sizeof(OM_OBJECTDATA)));
ASSERT((pData->length > 0) && (pData->length < OM_MAX_OBJECT_SIZE));
}
//
// Object IDs
//
// Internally, object IDs are a combination of a network ID and a four-byte
// sequence counter:
//
typedef struct tagOM_OBJECT_ID
{
TSHR_UINT32 sequence;
NET_UID creator; // MCS user ID of node which created it
WORD pad1;
} OM_OBJECT_ID;
typedef OM_OBJECT_ID * POM_OBJECT_ID;
//
// Partitioning of the first parameter on an event for INDICATION events
//
typedef struct tagOM_EVENT_DATA16
{
OM_WSGROUP_HANDLE hWSGroup;
OM_WORKSET_ID worksetID;
}
OM_EVENT_DATA16;
typedef OM_EVENT_DATA16 * POM_EVENT_DATA16;
//
// Partitioning of the second parameter on an event for CONFIRM events
//
typedef struct tagOM_EVENT_DATA32
{
WORD result;
OM_CORRELATOR correlator;
}
OM_EVENT_DATA32;
typedef OM_EVENT_DATA32 * POM_EVENT_DATA32;
//
//
// OBMANCONTROL
//
// This section describes the ObManControl Function Profile, as used by the
// Object Manager.
//
//
//
//
// DESCRIPTION
//
// In addition to the purely local records of workset groups, all instances
// of ObMan attached to a given Domain jointly maintain a control workset
// group containing
//
// - one workset (workset #0) listing the name, Function Profile, ID
// and MCS channel of each of the "standard" workset groups in
// the Domain, as well as the MCS user IDs of all the instances of ObMan
// in the Domain
//
// - one "registration workset" per workset group (worksets #1-#255)
// listing the MCS user IDs of the instances of ObMan which have one or
// more local Clients registered with the workset group.
//
// Creating a new workset group in a Domain causes ObMan to
//
// - add a new identification object to workset #0 and
//
// - create a new registration workset
//
// Registering with a workset group causes ObMan to
//
// - add a registration object to the appropriate registration workset.
//
//
//
// USAGE
//
// ObMan Clients can register with the ObManControl workset group, and then
// open and examine the contents of workset #0 to discover the names and
// Function Profiles of all the workset groups existing in a Domain.
//
// ObMan Clients must not attempt to lock or change the contents of this
// workset group in any way.
//
//
//
//
// OBJECT DEFINITIONS
//
// This section provides the definitions for the objects contained in the
// worksets of the ObManControl workset group.
//
//
typedef BYTE OM_WSGROUP_ID;
typedef OM_WSGROUP_ID * POM_WSGROUP_ID;
//
//
// WORKSET GROUP IDENTIFICATION OBJECT
//
// This structure identifies a workset within a Domain. Objects of this
// form reside in workset #0 of ObManControl, known as the INFO workset.
//
//
#define OM_INFO_WORKSET ((OM_WORKSET_ID) 0)
//
// NET PROTOCOL
//
typedef struct
{
TSHR_UINT32 length; // size of this structure, less four
// bytes (for length field itself)
DC_ID_STAMP idStamp; // == OM_WSGINFO_ID_STAMP
NET_CHANNEL_ID channelID; // workset group's MCS channel
NET_UID creator; // NET user ID of instance of ObMan
// which created workset group
OM_WSGROUP_ID wsGroupID; // Domain-unique ID
BYTE pad1;
WORD pad2;
char functionProfile[ OM_MAX_FP_NAME_LEN ];
// function profile
char wsGroupName[ OM_MAX_WSGROUP_NAME_LEN ];
// Client-supplied name
}
OM_WSGROUP_INFO;
typedef OM_WSGROUP_INFO * POM_WSGROUP_INFO;
#define OM_WSGINFO_ID_STAMP DC_MAKE_ID_STAMP('O', 'M', 'W', 'I')
void __inline ValidateObjectDataWSGINFO(POM_WSGROUP_INFO pInfoObj)
{
ValidateObjectData((POM_OBJECTDATA)pInfoObj);
ASSERT(pInfoObj->idStamp == OM_WSGINFO_ID_STAMP);
}
//
//
// WORKSET GROUP REGISTRATION OBJECTS
//
// This structure identifies a node's usage of a workset group. These
// objects can reside in any ObManControl workset.
//
// In the case of workset #0, these objects identify a node's usage of the
// ObManControl workset group itself. Since all instances of ObMan in a
// Domain are use the ObManControl workset group, the registration objects
// in workset #0 form a complete list of all the instances of ObMan in a
// Domain.
//
//
//
// NET PROTOCOL
//
typedef struct
{
TSHR_UINT32 length; // size of this structure, less four
// bytes (for length field itself)
DC_ID_STAMP idStamp; // == OM_WSGREGREC_ID_STAMP
NET_UID userID; // user ID of ObMan to which the
// object relates
TSHR_UINT16 status; // see below for status values
TSHR_PERSON_DATA personData;
}
OM_WSGROUP_REG_REC;
typedef OM_WSGROUP_REG_REC * POM_WSGROUP_REG_REC;
#define OM_WSGREGREC_ID_STAMP DC_MAKE_ID_STAMP('O', 'M', 'R', 'R')
void __inline ValidateObjectDataWSGREGREC(POM_WSGROUP_REG_REC pRegObject)
{
ValidateObjectData((POM_OBJECTDATA)pRegObject);
ASSERT(pRegObject->idStamp == OM_WSGREGREC_ID_STAMP);
}
//
// Value for <status> field:
//
#define CATCHING_UP 1
#define READY_TO_SEND 2
//
//
// LATE JOINER PROTOCOL
//
// If a Client registers with a workset group which already exists
// elsewhere in the Domain, that Client is considered a late joiner for
// that workset group. The protocol for bringing late joiners up to date
// is as follows (except where explicitly stated, "ObMan" means "the local
// instance of ObMan"):
//
// OVERVIEW
//
// A late-joiner node asks another "helper" node for a copy of the workset
// group. The helper node broadcasts a low-priority sweep message to all
// other nodes in the call and when it has received their replies, sends
// what it believes to be the current copy of the workset to the
// late-joiner.
//
// DETAILS
//
// At the local node, ObMan
//
// 1. locks the ObManControl workset group (one effect of this is that no
// other ObMan in the Domain will discard any workset groups it has
// local copies of)
//
// 2. examines the ObManControl workset group to determine
//
// - the MCS channel ID for the workset group
//
// - the MCS user ID of an instance of ObMan which has a copy of the
// workset group
//
// 3. requests to join the workset group channel
//
// 4. waits for the join to succeed
//
// 5. sends an OMNET_WSGROUP_SEND_REQ at high priority on the user ID
// channel of that instance of ObMan, known as the "helper"
//
// 6. broadcasts an OMNET_WORKSET_UNLOCK message at low priority to unlock
// the ObManControl workset group (on the ObManControl channel)
//
// At the helper node, ObMan
//
// 7. receives the OMNET_WSGROUP_SEND_REQ
//
// 8. marks its copy of the workset group as non-discardable
//
// 9. examines the ObManControl workset to determine the MCS user IDs
// of the remote instances of ObMan which already have copies
// of the workset group
//
// 10. broadcasts an OMNET_WSGROUP_SWEEP_REQ message on the workset group
// channel at high priority
//
// At each of the nodes queried in step 10, ObMan
//
// 11. receives the OMNET_WSGROUP_SWEEP_REQ
//
// 12. sends an OMNET_WSGROUP_SWEEP_REPLY message to the helper node at
// low priority
//
// Back at the helper node, ObMan
//
// 13. records each OMNET_WSGROUP_SWEEP_REPLY until all have been
// received*
//
// 14. sends one OMNET_WORKSET_NEW message for each workset in the workset
// group (on the late-joiners single-member channel)
//
// 15. sends an OMNET_OBJECT_ADD message for each object in each workset,
// again on the late-joiner's single member channel
//
// 16. sends an OMNET_WSGROUP_SEND_COMPLETE to the late-joiner; this
// message serves as a back marker for the late-joiner so that it
// knows when it has caught up with the state of the workset group as
// it was when it joined
//
// ASSUMPTIONS
//
// This protocol relies on the following assumptions:
//
// - The helper node receives the OMNET_WSGROUP_SEND_REQ message before
// the OMNET_WORKSET_UNLOCK message (as otherwise there is a window
// where its copy of the workset group may be discarded).
//
// This assumption is based on the fact that low-priority MCS data does
// not overtake high priority MCS data sent from the same node EVEN ON
// DIFFERENT CHANNELS.
//
// If this assumption proves invalid then either
//
// - the OMNET_WSGROUP_SEND_REQ message must be acknowledged before the
// late joiner can unlock the ObManControl workset, or
//
// - the OMNET_WSGROUP_SEND_REQ must be sent on the ObManControl
// broadcast channel, with an extra field indicating the node for which
// it is intended.
//
// - Any data received at the helper node after stage 14 begins is
// forwarded by MCS to the late-joiner.
//
// This assumption is based on the fact that the late-joiner is marked
// at the helper's MCS system as joined to the relevant channel before
// stage 14 begins. MCS guarantees that once a NET_CHANNEL_JOIN_IND has
// been received locally, the MCS system at every other node in the
// Domain is aware that the late-joiner has joined the channel.
//
// Note that in R1.1, the helper node will discover, at step 9, that there
// are no other nodes in the Domain. Therefore, steps 10-13 are eliminated
// i.e. the helper sends its copy of the workset as soon as it receives
// the request from the late-joiner.
//
// This is a major simplification and the code to implement these steps is
// not to be included in R1.1.
//
// * The glib "until all have been received" condition is actually
// difficult to implement since nodes may disappear while the helper is
// waiting. The solution to this is deferred to R2.0 (but see section on
// locking for suggested implementation).
//
//
//
//
// WORKSET LOCKING PROTOCOL
//
// In what follows, the "state" refers to the lock state of the workset, as
// stored in the workset record.
//
// At the locking node, ObMan does the following:
//
// 1. if the state is LOCK_GRANTED, post FAILURE then quit
//
// 2. examine the workset in ObManControl which corresponds to the
// workset group containing the workset to be locked, to determine the
// IDs of the other nodes which are in using the workset group (at most
// 1 node in R1.1); put these IDs in a list of "expected respondents"
// (Note: do not include own ID in this list)
//
// 3. if this list is empty, we have succeeded so post SUCCESS and quit
//
// 4. else broadcast an OMNET_WORKSET_LOCK_REQ message on the workset
// group channel (i.e. to each of these nodes)
//
// 5. set the workset state to LOCKING and post a delayed
// OMINT_EVENT_LOCK_TIMEOUT event
//
// At the other node(s), ObMan does the following:
//
// 6. receive the OMNET_WORKSET_LOCK_REQ message from the locking node
//
// 7. examine its current workset state
//
// 8. if it is LOCK_REQUESTED and the MCS user ID of the locking node is
// less than that of the current node, goto DENY
//
// 9. else, goto GRANT
//
// DENY:
//
// 10. send an OMNET_WORKSET_LOCK_DENY message to the locking node
//
// GRANT:
//
// 11. if the state if LOCKING, then we are giving the lock to a higher
// "priority" ObMan even though we wanted it ourselves, so post
// FAILURE
// locally (continue to 12)
//
// 12. set the state to LOCK_GRANTED
//
// 13. send an OMNET_WORKSET_LOCK_GRANT message to the locking node
//
// Back at the locking node, one of the following happens:
//
// ObMan receives an OMNET_WORKSET_LOCK_GRANT message
//
// it then deletes the ID of the node which sent it from the list of
// expected respondents
//
// if this list is now empty, all nodes have replied so post SUCCESS
// to Client
//
// OR
//
// ObMan receives an OMNET_WORKSET_LOCK_DENY message
//
// if the state is LOCKING, set it to READY, post FAILURE and quit
//
// if the state is anything else, this reply has come too late
// (we've timed out) so ignore it
//
// OR
//
// ObMan receives the OMINT_EVENT_LOCK_TIMEOUT event
//
// if the state is not LOCKING, the lock has succeeded so ignore the
// timeout
//
// otherwise, ObMan checks the ObManControl workset as in step 2 to
// see if any nodes still on the expected respondents list have in
// fact disappeared; if so post SUCCESS
//
// else post FAILURE.
//
//
//
// The state machine for the locking process is as follows (R1.1 version):
//
// |---------+-----------+---------+--------------|
// |UNLOCKED | LOCKING | LOCKED | LOCK_GRANTED |
// | 1 | 2 | 3 | 4 |
// |---------+-----------+---------+--------------|
// WorksetLock() |broadcast| FAIL | FAIL | FAIL |
// |LOCK_REQ,| | | |
// | ->2 | | | |
// |---------+-----------+---------+--------------|
// WorksetUnlock() | X |broadcast UNLOCK, ->1| X |
// |---------+-----------+---------+--------------|
// OMNET_WORKSET_LOCK_REQ |reply |compare |reply | - |
// |GRANT, |MCS IDs: |DENY | |
// | ->4 |if we're | | (in R1.1, |
// | |greater, | | this should |
// | |reply DENY | | be an error) |
// | |else reply | | |
// | |GRANT, ->4 | | |
// |---------+-----------+---------+--------------|
// OMNET_WORKSET_LOCK_GRANT| - |SUCCESS, | - | - |
// | | ->3 | | |
// |---------+-----------+---------+--------------|
// OMNET_WORKSET_LOCK_DENY | - |FAIL, ->1 | X | - |
// | | | | |
// |---------+-----------+---------+--------------|
// OMINT_EVENT_LOCK_TIMEOUT | - |if other | - | - |
// | |box gone, | | |
// | |SUCCESS, | | |
// | |->3, else | | |
// | |FAIL, ->1 | | |
// |---------+-----------+---------+--------------|
// OMNET_WORKSET_UNLOCK | - | - | X | ->1 |
// |---------+-----------+---------+--------------|
//
//
// where 'X' indicates an error condition and '-' indicates that the event
// or message is ignored.
//
//
//
//
// NOTES FOR R2.0 WORKSET LOCKING
//
// 1. If A tries to lock a workset and B grants the lock but C denies it,
// B will think that A has the lock. A has to broadcast an unlock,
// or else B has to realise that the conflict will be resolved in
// favour of C over A.
//
//
//
//
// DATA PROPAGATION and FLOW NOTIFICATION PROTOCOL
//
// When a local Client adds an object to a workset, or replaces or updates
// an existing object in a workset, ObMan broadcasts an appropriate
// OMNET_... message on the workset group channel.
//
// This header message identifies the object and the type of operation to
// be performed. It also includes a correlator value and the total size of
// the data to be sent in the following data packets.
//
// After sending the header, ObMan broadcasts one or more
// OMNET_GENERIC_DATA packets on the same channel. These packets, which
// are of arbitrary size, all contain the same correlator value as was
// included in the header packet.
//
// No sequence numbers are included as MCS guarantees that data packets
// sent at the same priority on the same channel from the same node will
// arrive at all other nodes in the sequence in which they were sent.
//
// It is the responsibility of the receiving node to detect when all data
// packets have arrived and then to insert, update or replace the object in
// the local copy of the workset.
//
// In addition, the receiving node, on receipt of EACH data packet sends a
// data acknowledgment message (OMNET_DATA_ACK) to the sending node (on its
// single-user channel), indicating the number of bytes received in that
// data packet.
//
//
//
//
// STANDARD OPERATION BROADCAST PROTOCOL
//
// When a local Client deletes or moves an object in a workset, or clears
// or creates a workset, ObMan broadcasts a single uncorrelated operation
// packet on the workset group channel.
//
// It is the responsibility of the receiving node to implement the
// operation locally.
//
//
//
//
// OPERATION SEQUENCING AND RESEQUENCING
//
// In order to consistently sequence operations which may arrive in
// different sequences at different nodes, each operation carries with it
// enough information for ObMan to reconstruct the workset, at each node,
// as if all the operations on it had arrived in the same sequence.
//
// To do this, all operations are assigned a sequence stamp before being
// broadcast. When ObMan receives an operation from the network, it
// compares its stamp to various stamps it maintains locally. Whether and
// how to perform the operation locally is determined on the basis of these
// comparisons, according to the rules defined below.
//
//
//
//
// SEQUENCE STAMPS AND THE WORKSET GENERATION NUMBER
//
// The sequencing order must be a globally consistent method of ordering
// events ("global" here refers to geographical distribution of all nodes
// operating on a given workset; it is not necessary that events be
// sequenced across different worksets, since operations on separate
// worksets can never interfere).
//
// We define an ObMan sequence stamp to be a combination of the workset
// generation number and the node id.
//
// The node ID is the user ID allocated by the MCS subsystem to the ObMan
// task and is therefore unique within a Domain.
//
// The workset generation number
//
// - is set to zero when the workset is created
//
// - is incremented each time ObMan performs an operation on behalf
// of a local Client
//
// - is, whenever an operation arrives from the network, set to the
// greater of its existing local value and the value in the
// operation's sequence stamp.
//
// The ordering of sequence stamps is defined as follows (notation: stampX
// = wsetGenX.nodeX):
//
// - if wsetGen1 < wsetGen2, then stamp1 < ("is lower than") stamp2
//
// - elsif wsetGen1 = wsetGen2, then
//
// - if node1 < node2, then stamp1 < stamp2
//
// - else stamp2 < stamp1.
//
// For the purposes of sequencing the different types of operations, ObMan
// maintains
//
// - one sequence stamp per workset:
//
// - the last 'time' it was cleared (the clear stamp)
//
// - four sequence stamps per object:
//
// - the 'time' the object was added (the addition stamp)
//
// - the 'time' the object was last moved (the position stamp)
//
// - the 'time' the object was last updated (the update stamp)
//
// - the 'time' the object was last replace (the replace stamp; in
// reality, only one of the update/replace stamps is required for
// sequencing but both are needed for optimum spoiling).
//
// The initial values of the position, update and replace stamps are set to
// the value of the addition stamp.
//
// The initial value of the clear stamp is set to <0.ID> where ID is the ID
// of the node which created the workset.
//
// In addition, each object has a position field (either FIRST or LAST in
// R1.1) which indicates where the object was most recently positioned i.e.
// it is set when the object is added and then each time the object is
// moved within the workset.
//
//
//
//
// SEQUENCING PROTOCOLS
//
// The treatment of each type of operation is now considered in turn.
//
// 1. Operations on unknown objects or worksets
//
// ObMan may at any time receive operations on objects or worksets which do
// not exist locally. These operations may be on objects or worksets which
//
// - this node has not yet heard of, or
//
// - have been deleted.
//
// Operations on the first kind need to be delayed and reprocessed at a
// later time. Operations on the second kind can be thrown away (note that
// there are no workset operations of this kind as once opened, worksets
// are never deleted in the lifetime of the workset group).
//
// To differentiate between the two, ObMan keeps a record of deleted
// objects. When an operation on a deleted object arrives, it is
// discarded. When an operation arrives for an object which is not in
// either the active object list or the deleted object list, ObMan bounces
// the event from the network layer back to its event loop, with a suitable
// delay, and attempts to process it later.
//
// For simplicity, the deleted object list is implemented by flagging
// deleted objects as such and leaving them in the main list of objects
// (i.e. the workset), rather than moving them to a separate list. The
// object data is, however, discarded; only the object record needs to be
// kept.
//
// Events from the network layer referring to operations on unknown
// worksets are automatically bounced back onto ObMan's event queue.
//
// 2. Adding an object
//
// If ObMan receives an Add operation for an object which it has already
// added to a workset (i.e. the object IDs are the same), it discards the
// operation.
//
// This normally will not happen since each object is added by only one
// node, and no node adds an object with the same ID twice.
//
// However, while a late-joiner is catching up with the contents of a
// workset, it is possible that it will receive notification of a
// particular object from both
//
// - the node which added the object
//
// - the helper node which is sending it the entire workset
// contents.
//
// Therefore, the late-joiner checks object IDs as they arrive and discards
// them if they have already been received. Note that since the
// positioning algorithm presented below will position each occurrence of
// the object in adjacent positions, checking for ID clashes is a simple
// matter, performed after the correct position has been found.
//
// 3. Positioning (adding or moving) an object in a workset
//
// The desired sequence of objects in a workset is defined to be one
// whereby
//
// - all the objects which were positioned at the start of a workset (FIRST
// objects) are before all the objects which were positioned at the end
// of a workset (LAST objects)
//
// - the position stamps of all the FIRST objects decrease monotonically
// from the start of the workset forward
//
// - the position stamps of all the LAST objects decrease monotonically
// from the end of the workset backward.
//
// Accordingly, the protocol when positioning an object at the start of a
// workset is as follows (instructions for end-of-workset positioning in
// brackets):
//
// ObMan searches forward (back) from the start (end) of the workset until
// if finds an object which either
//
// - is not a FIRST (LAST) object, or
//
// - has a lower (lower) position stamp
//
// ObMan inserts the new/moved object before (after) this object.
//
// 4. Clearing a workset
//
// On receiving a Clear operation, ObMan searches through the workset and
// deletes all objects which have an addition stamp lower than the clear
// operation's stamp.
//
// On receiving an addition to a workset, ObMan discards the operation if
// its stamp is lower than the workset's clear stamp.
//
// 5. Updating an object
//
// On receiving an Update operation, ObMan compares its stamp with the
// object's update and replace stamps. If the operation's stamp is higher
// than both, the operation is performed; otherwise, the operation is
// discarded (since an Update is superceded either by a later Replace or by
// a later Update).
//
// 6. Replacing an object
//
// On receiving a Replace operation, ObMan compares its stamp with the
// object's replace stamp. If the operation's stamp is higher, the
// operation is performed; otherwise, the operation is discarded (since a
// Replace is superceded by a later Replace but not by a later Update).
//
// 7. Deleting an object
//
// By definition, a Delete is the last operation that should be performed
// on an object. Delete operations are therefore processed immediately by
// setting the <deleted> flag in the object record to TRUE.
//
//
//
//
// OPERATION RESEQUENCING - SUMMARY
//
// In summary, therefore,
//
// - all object operations are discarded if object found on the deleted
// object queue
//
// - Add operations are discarded if they refer to an existing object.
//
// - Add/Clear operations are requeued if the workset is not present
// locally
//
// - Update/Replace/Move/Delete operations are requeued if the object or
// workset is not present locally
//
// - Update operations are discarded if an Update or a Replace with a later
// sequence stamp has already been received.
//
// - Replace operations are discarded if a Replace with a later sequence
// stamp has already been received.
//
// By default, all operations are performed.
//
//
//
//
// OBJECT IDS
//
// Object IDs are structures which identify an object within a workset.
// For a given workset, they are unique throughout the Domain.
//
// To ensure uniqueness, the MCS user ID is used as a (two-byte) prefix to
// a four-byte sequence number generated locally, on a per workset basis.
//
// Workset groups can exist independently of a Domain, and therefore
// potentially before ObMan has been allocated an MCS user ID. When
// allocating object IDs in this situation, ObMan uses zero (0) as the
// prefix to the sequence number.
//
// If that workset group is subsequently moved into a Domain, for all
// subsequent ID allocations ObMan uses its MCS user ID for that Domain as
// the prefix. Other instances of ObMan may also start adding objects to
// the worksets in the group at this point, and they too use their MCS user
// IDs as the object ID prefix. Uniqueness is preserved by the MCS
// guarantee that zero is never a valid user ID, so no post-share generated
// ID can conflict with a pre-share generated ID.
//
//
//
//
// SEQUENCE STAMPS
//
// Sequence stamps define a Domain-wide ordering for operations. They are
// used to correctly execute operations which may arrive at a node in an
// indeterminate order.
//
//
typedef struct tagOM_SEQUENCE_STAMP
{
TSHR_UINT32 genNumber; // the workset generation number
// which was current when the
// stamp was issued
NET_UID userID; // the MCS user ID for ObMan at
// the node which issued it
WORD pad1;
} OM_SEQUENCE_STAMP;
typedef OM_SEQUENCE_STAMP * POM_SEQUENCE_STAMP;
//
//
// OBJECT POSITION STAMPS
//
// When an object is added to or moved within a workset, it is important to
// know where it has been added. Therefore, Add and Move operations
// include within them a position field, with the following type:
//
//
typedef BYTE OM_POSITION;
//
// Possible values for an OM_POSITION variable:
//
#define LAST 1
#define FIRST 2
#define BEFORE 3
#define AFTER 4
//
//
// SEQUENCE STAMP MANIPULATION
//
// These macro manipulate sequence stamps.
//
//
//
//
// STAMP_IS_LOWER(stamp1, stamp2)
//
// This macro compares one sequence stamp with another. It evaluates to
// TRUE if the first stamp is lower than the second.
//
//
#define STAMP_IS_LOWER(stamp1, stamp2) \
\
(((stamp1).genNumber < (stamp2).genNumber) ? \
TRUE : \
(((stamp1).genNumber == (stamp2).genNumber) \
&& \
((stamp1).userID < (stamp2).userID)))
//
//
// SET_NULL_SEQ_STAMP(stamp)
//
// This macro sets the sequence stamp <stamp> to NULL.
//
//
#define SET_NULL_SEQ_STAMP(stamp) \
\
(stamp).userID = 0; \
(stamp).genNumber = 0
//
//
// SEQ_STAMP_IS_NULL(stamp)
//
// This macro evaluates to TRUE if the sequence stamp <stamp> is a NULL
// sequence stamp.
//
//
#define SEQ_STAMP_IS_NULL(stamp) \
\
((stamp.userID == 0) && (stamp.genNumber == 0))
//
//
// COPY_SEQ_STAMP(stamp1, stamp2)
//
// This macro sets the value of the first sequence stamp to that of the
// second.
//
//
#define COPY_SEQ_STAMP(stamp1, stamp2) \
\
(stamp1).userID = (stamp2).userID; \
(stamp1).genNumber = (stamp2).genNumber
//
//
// MESSAGE FORMATS
//
// This section describes the formats of the messages sent between
// different instances of ObMan.
//
// The names of these messages are prefixed OMNET_...
//
// These events have the following format:
//
// typedef struct
// {
// OMNET_PKT_HEADER header;
// :
// : [various event specific fields]
// :
//
// } OMNET_...
//
// The OMNET_PKT_HEADER type is defined below.
//
//
typedef TSHR_UINT16 OMNET_MESSAGE_TYPE;
typedef struct tagOMNET_PKT_HEADER
{
NET_UID sender; // MCS user ID of sender
OMNET_MESSAGE_TYPE messageType; // == OMNET_...
}
OMNET_PKT_HEADER;
typedef OMNET_PKT_HEADER * POMNET_PKT_HEADER;
//
// Possible values for a OMNET_MESSAGE_TYPE variable:
//
#define OMNET_NULL_MESSAGE ((OMNET_MESSAGE_TYPE) 0x00)
#define OMNET_HELLO ((OMNET_MESSAGE_TYPE) 0x0A)
#define OMNET_WELCOME ((OMNET_MESSAGE_TYPE) 0x0B)
#define OMNET_LOCK_REQ ((OMNET_MESSAGE_TYPE) 0x15)
#define OMNET_LOCK_GRANT ((OMNET_MESSAGE_TYPE) 0x16)
#define OMNET_LOCK_DENY ((OMNET_MESSAGE_TYPE) 0x17)
#define OMNET_UNLOCK ((OMNET_MESSAGE_TYPE) 0x18)
#define OMNET_LOCK_NOTIFY ((OMNET_MESSAGE_TYPE) 0x19)
#define OMNET_WSGROUP_SEND_REQ ((OMNET_MESSAGE_TYPE) 0x1E)
#define OMNET_WSGROUP_SEND_MIDWAY ((OMNET_MESSAGE_TYPE) 0x1F)
#define OMNET_WSGROUP_SEND_COMPLETE ((OMNET_MESSAGE_TYPE) 0x20)
#define OMNET_WSGROUP_SEND_DENY ((OMNET_MESSAGE_TYPE) 0x21)
#define OMNET_WORKSET_CLEAR ((OMNET_MESSAGE_TYPE) 0x28)
#define OMNET_WORKSET_NEW ((OMNET_MESSAGE_TYPE) 0x29)
#define OMNET_WORKSET_CATCHUP ((OMNET_MESSAGE_TYPE) 0x30)
#define OMNET_OBJECT_ADD ((OMNET_MESSAGE_TYPE) 0x32)
#define OMNET_OBJECT_CATCHUP ((OMNET_MESSAGE_TYPE) 0x33)
#define OMNET_OBJECT_REPLACE ((OMNET_MESSAGE_TYPE) 0x34)
#define OMNET_OBJECT_UPDATE ((OMNET_MESSAGE_TYPE) 0x35)
#define OMNET_OBJECT_DELETE ((OMNET_MESSAGE_TYPE) 0x36)
#define OMNET_OBJECT_MOVE ((OMNET_MESSAGE_TYPE) 0x37)
#define OMNET_MORE_DATA ((OMNET_MESSAGE_TYPE) 0x46)
//
//
// GENERIC OPERATION PACKET
//
// ObMan uses this structure for the following messages:
//
// OMNET_MORE_DATA uses first 1 field (4 bytes), plus data
//
// OMNET_WORKSET_NEW } use first 7 fields (24 bytes)
// OMNET_WORKSET_CATCHUP }
//
// OMNET_WORKSET_CLEAR uses first 6 fields (16 bytes);
// doesn't use <position), <flags>
//
// OMNET_OBJECT_MOVE uses first 7 fields (24 bytes);
// doesn't use <flags>
//
// OMNET_OBJECT_DELETE uses first 7 fields (24 bytes);
// doesn't use <position), <flags>
//
// OMNET_OBJECT_REPLACE } use first 8 fields (28 bytes), plus
// OMNET_OBEJCT_UPDATE } data; don't use <position), <flags>
//
// OMNET_OBJECT_ADD uses first 9 fields (32 bytes), plus
// data; doesn't use <flags>
//
// OMNET_OBJECT_CATCHUP uses all 12 fields (56 bytes), plus
// data
//
//
typedef struct tagOMNET_OPERATION_PKT
{
OMNET_PKT_HEADER header;
OM_WSGROUP_ID wsGroupID;
OM_WORKSET_ID worksetID;
BYTE position; // <position> for Add/Move/Catchup
BYTE flags; // <flags> for ObjectCatchUp
//
// Note: for WORKSET_NEW/CATCHUP messages, the two bytes occupied by
// the <position> and <flags> fields hold a NET_PRIORITY value.
//
OM_SEQUENCE_STAMP seqStamp; // operation sequence stamp
// (== addStamp for ObjectCatchUp,
// curr stamp for WorksetCatchUp)
OM_OBJECT_ID objectID;
//
// Note: for WORKSET_NEW/CATCHUP messages, the first byte occupied
// by the <objectID> field holds a BOOL indicating whether the
// workset is persistent.
//
TSHR_UINT32 totalSize; // total size of transfer
TSHR_UINT32 updateSize;
OM_SEQUENCE_STAMP positionStamp;
OM_SEQUENCE_STAMP replaceStamp;
OM_SEQUENCE_STAMP updateStamp;
}
OMNET_OPERATION_PKT;
typedef OMNET_OPERATION_PKT * POMNET_OPERATION_PKT;
#define OMNET_MORE_DATA_SIZE 4
#define OMNET_WORKSET_NEW_SIZE 24
#define OMNET_WORKSET_CATCHUP_SIZE 24
#define OMNET_WORKSET_CLEAR_SIZE 16
#define OMNET_OBJECT_MOVE_SIZE 24
#define OMNET_OBJECT_DELETE_SIZE 24
#define OMNET_OBJECT_REPLACE_SIZE 28
#define OMNET_OBJECT_UPDATE_SIZE 28
#define OMNET_OBJECT_ADD_SIZE 32
#define OMNET_OBJECT_CATCHUP_SIZE 56
//
// These define the sizes of the packets we used in R1.1: we must only send
// packets of this size to R1.1 systems.
//
//
//
// HELLO/WELCOME MESSAGE
//
// When ObMan attaches to a Domain that contains an outgoing call, it
// broadcasts an OMNET_WELCOME message on the well-known ObManControl
// channel.
//
// When ObMan attaches to a Domain that contains an incoming call, it
// broadcasts an OMNET_HELLO message on the well-known ObManControl
// channel.
//
// When ObMan receives a HELLO message, it replies with a WELCOME message,
// just as if it had just joined the call.
//
// This allows each late-joining ObMan in the call to discover the user ID
// of each of the other instances of ObMan.
//
// A late-joining ObMan uses this information by asking one of the nodes
// which WELCOMEd it for a copy of the ObManControl workset group.
//
// HELLO/WELCOME packets are NEVER compressed.
//
// WELCOME and HELLO messages have the following format:
//
//
typedef struct tagOMNET_JOINER_PKT
{
OMNET_PKT_HEADER header;
TSHR_UINT32 capsLen; // == 4 in this version.
TSHR_UINT32 compressionCaps; // bitwise OR of OM_CAPS_ bits
}
OMNET_JOINER_PKT;
typedef OMNET_JOINER_PKT * POMNET_JOINER_PKT;
//
// The actual compression type used in any given packet is specified as the
// first byte of the packet (before the header and other structures
// specified in this file). The compression type is the numeric value of
// the bit position corresponding to the compression capability. For
// example, if XYZ compression has a capability value of 0x8, then packets
// compressed with XYZ will have 3 in their first byte.
//
// '0' is never valid as an OM_PROT_... compression type (which is why bit
// 1 is not used as an OM_CAPS_... flag).
//
#define OM_PROT_PKW_COMPRESSED 0x01
#define OM_PROT_NOT_COMPRESSED 0x02
//
// Values for compressionCaps. These must be separate bits, since they may
// be ORed together if a node supports multiple compression types.
//
// Note that OM_CAPS_NO_COMPRESSION is always supported.
//
// Bit 1 is not used.
//
#define OM_CAPS_PKW_COMPRESSION 0x0002
#define OM_CAPS_NO_COMPRESSION 0x0004
//
//
// LATE-JOINER PROTOCOL - WORKSET GROUP SEND REQUEST/SEND COMPLETE
//
// The SEND_REQUEST message is sent when ObMan has "late-joined" a
// particular workset group and would like another node to send it the
// current contents.
//
// The message is sent to an arbitrary "helper" node (known to have a copy
// of the workset group) on its single-user channel.
//
// The recipient responds by flushing the relevant channel (in R1.1, a
// no-op; in R2.0, perform a WORKSET_CHECKPOINT) and then sending the
// contents of the workset.
//
// When the WORKSET_CATCHUP messages have been sent, we send a
// WSGROUP_SEND_MIDWAY message to let the late joiner know that it now
// knows about all the worksets which are currently in use (otherwise, it
// might create a workset already in use which happened to be locked on the
// sending machine add then an object to it.
//
// The SEND_MIDWAY message also containing the max sequence number
// previously used by the late joiner's ID in this workset group (to
// prevent re-use of object IDs).
//
// When the contents have been sent, i.e. after the last data packet of
// the last object in the last workset of the group, the helper sends a
// SEND_COMPLETE message.
//
// If the chosen helper node is not in a position to send the contents of
// the workset group, it must repsond with a SEND_DENY message, upon
// receipt of which the late joiner will choose someone else to catch up
// from.
//
// The SEND_REQUEST, SEND_MIDWAY, SEND_COMPLETE and SEND_DENY message
// packets have the following structure:
//
//
typedef struct tagOMNET_WSGROUP_SEND_PKT
{
OMNET_PKT_HEADER header;
OM_WSGROUP_ID wsGroupID;
BYTE pad1;
TSHR_UINT16 correlator; // Holds the catchup correlator
OM_OBJECT_ID objectID;
TSHR_UINT32 maxObjIDSeqUsed;
}
OMNET_WSGROUP_SEND_PKT;
typedef OMNET_WSGROUP_SEND_PKT * POMNET_WSGROUP_SEND_PKT;
//
//
// LOCKING PROTOCOL - LOCK PACKET
//
// When ObMan wants to lock a workset/object, it broadcasts one of these
// packets (with type == OMNET_LOCK_REQ).
//
// When ObMan receives one of these packets, it decides to deny or grant
// the lock to the sender, and replies with another packet of the same
// structure but with type == OMNET_LOCK_DENY or OMNET_LOCK_GRANT as
// appropriate.
//
// When ObMan wants to unlock a workste/object it has previously locked, it
// broadcasts one of these packets with type == OMNET_UNLOCK.
//
typedef struct tagOMNET_LOCK_PKT
{
OMNET_PKT_HEADER header;
OM_WSGROUP_ID wsGroupID;
OM_WORKSET_ID worksetID;
TSHR_UINT16 data1; // used as correlator for GRANT/DENY
// used to indicate who's got lock
// for NOTIFY
// lon: need to keep pLockReqCB field for backward compatability!
void * pLockReqCB; // R1.1 uses this to find the lock
// request CB
}
OMNET_LOCK_PKT;
typedef OMNET_LOCK_PKT * POMNET_LOCK_PKT;
//
//
// DATA STRUCTURES
//
//
//
//
// OPERATION TYPES
//
// This is the type defined for operations which Clients may perform on
// objects and worksets. Pending operation lists use this type.
//
//
typedef TSHR_UINT16 OM_OPERATION_TYPE;
//
// Possible values for a OM_OPERATION_TYPE variable:
//
#define NULL_OP ((OM_OPERATION_TYPE) 0)
#define WORKSET_CLEAR ((OM_OPERATION_TYPE) 1)
#define OBJECT_ADD ((OM_OPERATION_TYPE) 2)
#define OBJECT_MOVE ((OM_OPERATION_TYPE) 3)
#define OBJECT_DELETE ((OM_OPERATION_TYPE) 4)
#define OBJECT_REPLACE ((OM_OPERATION_TYPE) 5)
#define OBJECT_UPDATE ((OM_OPERATION_TYPE) 6)
//
//
// PENDING OPERATION LISTS
//
// When ObMan receives a request (either from a local Client or over the
// network) to delete, update or replace an object, or to clear a workset,
// it cannot perform the operation until the local Client has confirmed it.
// These operations are therefore put in a list and processed when the
// appropriate Confirm function is invoked.
//
// This list is hung off the workset record; its elements have the
// following format:
//
//
typedef struct tagOM_PENDING_OP
{
STRUCTURE_STAMP
BASEDLIST chain;
POM_OBJECT pObj; // NULL if a clear operation
POM_OBJECTDATA pData;
OM_SEQUENCE_STAMP seqStamp; // the sequence stamp which was
// current in the workset when the
// operation was invoked
OM_OPERATION_TYPE type; // == WORKSET_CLEAR, OBJECT_DELETE,
// OBJECT_UPDATE or OBJECT_REPLACE
WORD pad1;
}
OM_PENDING_OP;
typedef OM_PENDING_OP * POM_PENDING_OP;
//
//
// OBJECT RECORDS
//
// This structure holds information about a particular object,
//
typedef struct tagOM_OBJECT
{
STRUCTURE_STAMP
BASEDLIST chain;
OM_OBJECT_ID objectID; // Unique within domain
POM_OBJECTDATA pData; // Ptr to data
OM_SEQUENCE_STAMP addStamp; // the sequence stamps used
OM_SEQUENCE_STAMP positionStamp; OM_SEQUENCE_STAMP replaceStamp;
OM_SEQUENCE_STAMP updateStamp;
UINT updateSize; // size of (all) updates
BYTE flags; // defined below
OM_POSITION position; // either LAST or FIRST,
// indicating where the object
// was most recently placed
WORD pad1;
}
OM_OBJECT;
BOOL __inline ValidObject(POM_OBJECT pObj)
{
return(!IsBadWritePtr(pObj, sizeof(OM_OBJECT)));
}
void __inline ValidateObject(POM_OBJECT pObj)
{
ASSERT(ValidObject(pObj));
}
//
// Flags used:
//
#define DELETED 0x0001
#define PENDING_DELETE 0x0002
//
//
// UNUSED OBJECTS LISTS
//
// When a Client allocates an object using OM_ObjectAlloc, a reference to
// the memory allocated is stored in the Client's unused objects list for
// this workset group.
//
// The reference is removed when the Client either
//
// - discards the object using OM_ObjectDiscard, or
//
// - inserts the object in a workset with an Add, Update or Replace
// function.
//
// This list of objects (which is hung off the usage record) is checked
// when a workset is closed to discard any objects the Client didn't
// explicitly discard or use.
//
// The elements of the list have the following form:
//
//
typedef struct tagOM_OBJECTDATA_LIST
{
STRUCTURE_STAMP
BASEDLIST chain;
POM_OBJECTDATA pData;
UINT size; // Used to verify that Client hasn't grown size
OM_WORKSET_ID worksetID;
BYTE pad1;
WORD pad2;
}
OM_OBJECTDATA_LIST;
typedef OM_OBJECTDATA_LIST * POM_OBJECTDATA_LIST;
//
//
// OBJECTS-IN-USE LISTS
//
// When a Client reads an object using OM_ObjectRead, the use count of the
// chunk of memory containing the object is increased.
//
// The use count is deceremented again when the Client calls
// OM_ObjectRelease, but if the Client abends, or simply closes a workset
// without releasing the objects it has read, we still need to be able to
// free the memory.
//
// Therefore we keep a list, on a per-workset-group basis, of the objects
// that a Client is using. Objects (identified by handles) are added to
// the list when a Client calls OM_ObjectRead, and removed from the list
// when the Client calls OM_ObjectRelease.
//
// In addition, the list is checked when a workset is closed to release any
// handles the Client didn't release explicitly.
//
// Like the unused objects list, this list is hung off the usage record.
//
// The elements of these lists have the following form:
//
//
typedef struct tagOM_OBJECT_LIST
{
STRUCTURE_STAMP
BASEDLIST chain;
POM_OBJECT pObj;
OM_WORKSET_ID worksetID; // the ID of the workset
BYTE pad1;
WORD pad2;
}
OM_OBJECT_LIST;
typedef OM_OBJECT_LIST * POM_OBJECT_LIST;
//
//
// NODE LIST STRUCTURES
//
// When requesting locks etc. from other nodes in a Domain, ObMan keeps a
// list of remote nodes which it expects a reply from. A node is
// identified by the MCS user ID of the instance of ObMan running on that
// node.
//
// The elements of these lists have the folllowing form:
//
//
typedef struct tagOM_NODE_LIST
{
STRUCTURE_STAMP
BASEDLIST chain;
NET_UID userID; // user ID of remote ObMan
WORD pad1;
}
OM_NODE_LIST;
typedef OM_NODE_LIST * POM_NODE_LIST;
//
//
// LOCK REQUEST CONTROL BLOCKS
//
// When ObMan is in the process of getting a lock for a workset or object
// it creates one of these structures to correlate the lock replies.
//
//
typedef struct tagOM_LOCK_REQ
{
STRUCTURE_STAMP
BASEDLIST chain;
PUT_CLIENT putTask; // task to notify on success
// or failure
OM_CORRELATOR correlator; // returned by WorksetLockReq
OM_WSGROUP_ID wsGroupID; // workset group and workset
OM_WORKSET_ID worksetID; // containing the lock
POM_WSGROUP pWSGroup;
BASEDLIST nodes; // MCS user IDs of nodes which
// haven't yet replies to req
// (an OM_NODE_LIST list)
WORD retriesToGo; // Decremented on each timeout
OM_WSGROUP_HANDLE hWSGroup;
BYTE type; // PRIMARY or SECONDARY
}
OM_LOCK_REQ;
typedef OM_LOCK_REQ * POM_LOCK_REQ;
#define LOCK_PRIMARY 0x01
#define LOCK_SECONDARY 0x02
//
//
// CLIENT LIST STRUCTURE
//
// The lists of Clients stored per workset group and per workset contain
// elements of this type. The <hWSGroup> field refers to the workset group
// handle by which the Client knows the workset group concerned.
//
//
typedef struct tagOM_CLIENT_LIST
{
STRUCTURE_STAMP
BASEDLIST chain;
PUT_CLIENT putTask; // the Client's putTask
WORD mode;
OM_WSGROUP_HANDLE hWSGroup;
BYTE pad1;
}
OM_CLIENT_LIST;
typedef OM_CLIENT_LIST * POM_CLIENT_LIST;
//
//
// WORKSET RECORDS
//
// This structure holds the state information for a workset. It resides at
// offset zero (0) in the huge memory block associated with this workset.
//
// ObMan allocates a workset record when a workset is created and discards
// it when the workset is discarded.
//
//
typedef struct tagOM_WORKSET
{
STRUCTURE_STAMP
UINT numObjects; // the current number of objects in
// workset (excluding the sentinels)
UINT genNumber; // current workset generation number
OM_SEQUENCE_STAMP clearStamp; // the clear stamp for the workset
NET_PRIORITY priority; // MCS priority for the workset
OM_WORKSET_ID worksetID;
BYTE lockState; // one of the values defined below
WORD lockCount; // LOCAL lock count
NET_UID lockedBy; // MCS user ID of node which has the
// lock, if any
BASEDLIST objects; // root of list of workset's objects
UINT bytesUnacked; // bytes still to be acked
BASEDLIST pendingOps; // root of list of operations which
// are pending for this workset
BASEDLIST clients; // root of list of Clients which
// have this workset open
BOOL fTemp;
}
OM_WORKSET;
typedef OM_WORKSET * POM_WORKSET;
void __inline ValidateWorkset(POM_WORKSET pWorkset)
{
ASSERT(!IsBadWritePtr(pWorkset, sizeof(OM_WORKSET)));
}
//
// Possible values for the <lockState> field above:
//
#define UNLOCKED 0x00
#define LOCKING 0x01
#define LOCKED 0x02
#define LOCK_GRANTED 0x03
//
//
// WORKSET GROUP RECORDS
//
// This structure holds information about a workset group.
//
// ObMan maintains one of these structures for each workset group with
// which one or more local Clients are registered.
//
// It will be discarded when the last local Client registered with the
// workset group deregisters from it.
//
//
typedef struct tagOM_WSGROUP
{
STRUCTURE_STAMP
BASEDLIST chain;
OMWSG wsg;
OMFP fpHandler;
NET_CHANNEL_ID channelID; // MCS channel ID used for WSG
OM_WSGROUP_ID wsGroupID; // workset group ID
BYTE state; // one of the values defined below
POM_OBJECT pObjReg; // Registration object in the OMC workset
BASEDLIST clients; // the Clients using the WSG
POM_DOMAIN pDomain;
NET_UID helperNode; // ID of the node we are catching up from.
WORD valid:1;
WORD toBeDiscarded:1;
UINT bytesUnacked; // sum of bytesUnacked field for each
// workset in the workset group
BYTE sendMidwCount; // # of SEND_MIDWAYs received
BYTE sendCompCount; // # of SEND_MIDWAYs received
OM_CORRELATOR catchupCorrelator; // Used to correlate SEND_REQUESTS
// to SEND_MIDWAYs and
// SEND_COMPLETEs.
POM_WORKSET apWorksets[ OM_MAX_WORKSETS_PER_WSGROUP + 1];
}
OM_WSGROUP;
void __inline ValidateWSGroup(POM_WSGROUP pWSGroup)
{
ASSERT(!IsBadWritePtr(pWSGroup, sizeof(OM_WSGROUP)));
}
//
// Workset group <state> values
//
#define INITIAL 0x00
#define LOCKING_OMC 0x01
#define PENDING_JOIN 0x02
#define PENDING_SEND_MIDWAY 0x03
#define PENDING_SEND_COMPLETE 0x04
#define WSGROUP_READY 0x05
//
//
// USAGE RECORDS
//
// A usage record identifies a Client's use of a particular workset group
// and holds state information about that usage.
//
// Usage records reside in the OMGLBOAL memory block at the offset (from
// the base) specified in the Client record.
//
//
typedef struct tagOM_USAGE_REC
{
STRUCTURE_STAMP
POM_WSGROUP pWSGroup; // Client pointer to workset group
BASEDLIST unusedObjects; // start sentinel in list of
// pointers to unused objects
BASEDLIST objectsInUse; // OM_OBJECT_LIST
BYTE mode;
BYTE flags;
BYTE worksetOpenFlags[(OM_MAX_WORKSETS_PER_WSGROUP + 7)/8];
// bitfield array of flags
// indicating the worksets the
// Client has open
}
OM_USAGE_REC;
typedef OM_USAGE_REC * POM_USAGE_REC;
__inline void ValidateUsageRec(POM_USAGE_REC pUsageRec)
{
ASSERT(!IsBadWritePtr(pUsageRec, sizeof(OM_USAGE_REC)));
}
//
// Values for flags:
//
#define ADDED_TO_WSGROUP_LIST 0x0002
#define PWSGROUP_IS_PREGCB 0x0004
//
//
// LOCK STACKS
//
// Clients must request and release object and workset locks in accordance
// with the Universal Locking Order as defined in the Functional Spec (in
// order to avoid deadlock).
//
// In order to detect lock order violations, ObMan maintains, for each
// Client, a stack of locks which the Client holds or has requested. This
// stack is implemented as a linked list, with the most recently acquired
// lock (which must, by definition, be the one latest in the Universal
// Locking Order) being the first element.
//
// A Client's lock stack is initialised when the Client registers with
// ObMan and discarded when the Client deregisters from ObMan. Lock stacks
// are hung off the Client record.
//
// Note that we need the store the object ID here, as opposed to the
// handle, since we must enforce the universal lock order across all nodes.
//
// Elements of lock stacks have the following form:
//
//
typedef struct tagOM_LOCK
{
STRUCTURE_STAMP
BASEDLIST chain;
POM_WSGROUP pWSGroup; // Client pointer to workset group
// needed to detect lock violations
OM_OBJECT_ID objectID; // the object ID is 0 if this is a
// workset lock (in R1.1, always).
OM_WORKSET_ID worksetID;
BYTE pad1;
WORD pad2;
}
OM_LOCK;
typedef OM_LOCK * POM_LOCK;
int __inline CompareLocks(POM_LOCK pLockFirst, POM_LOCK pLockSecond)
{
int result;
result = (pLockSecond->pWSGroup->wsg - pLockFirst->pWSGroup->wsg);
if (result == 0)
{
// Same WSG, so compare worksets
result = (pLockSecond->worksetID - pLockFirst->worksetID);
}
return(result);
}
//
//
// CLIENT RECORD
//
// ObMan maintains instance data for every registered Client. This
// structure, a Client record, holds the Client instance data.
//
// A Client's ObMan handle is a Client pointer to this structure.
//
// A Client's workset group handle is an index into the array of usage record
// ptrs.
//
// If the value of apUsageRecs is 0 or -1, x is not a valid workset
// group handle.
//
//
typedef struct tagOM_CLIENT
{
STRUCTURE_STAMP
PUT_CLIENT putTask;
BOOL exitProcReg:1;
BOOL hiddenHandlerReg:1;
BASEDLIST locks; // root of list of locks held
POM_USAGE_REC apUsageRecs[OMWSG_MAXPERCLIENT];
BOOL wsgValid[OMWSG_MAXPERCLIENT];
}
OM_CLIENT;
BOOL __inline ValidWSGroupHandle(POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup)
{
return((hWSGroup != 0) &&
(pomClient->wsgValid[hWSGroup]) &&
(pomClient->apUsageRecs[hWSGroup] != NULL));
}
//
//
// DOMAIN RECORD
//
// This structure holds information about a Domain. We support two:
// * The current call
// * Limbo (no call) for cleanup after a call and maintenance of info
// across calls
//
typedef struct tagOM_DOMAIN
{
STRUCTURE_STAMP
BASEDLIST chain;
UINT callID; // MCS Domain Handle
NET_UID userID; // ObMan's MCS user ID and token ID
NET_TOKEN_ID tokenID; // for this domain
NET_CHANNEL_ID omcChannel; // ObMan's broadcast channel
BYTE state; // one of the values defined below
BYTE omchWSGroup; // ObMan's hWSGroup for this domain's
BOOL valid:1;
BOOL sendEventOutstanding:1;
UINT compressionCaps; // Domain-wide compression caps
BASEDLIST wsGroups; // root of list of workset groups
BASEDLIST pendingRegs; // root of list of pending workset
// group registration request
BASEDLIST pendingLocks; // root of list of pending
// lock requests
BASEDLIST receiveList; // root of list of control blocks
// for receives in progress
BASEDLIST bounceList; // root of list of control blocks
// for bounced messages
BASEDLIST helperCBs; // root of list of helper CBs for
// checkpoints in progress
BASEDLIST sendQueue[NET_NUM_PRIORITIES];
// array of roots of list of send
// queue instructions (by priority)
BOOL sendInProgress[NET_NUM_PRIORITIES];
// array of send-in-progress flags
}
OM_DOMAIN;
//
// Possible values for <state> field:
//
#define PENDING_ATTACH 0x01
#define PENDING_JOIN_OWN 0x02
#define PENDING_JOIN_OMC 0x03
#define PENDING_TOKEN_ASSIGN 0x04
#define PENDING_TOKEN_GRAB 0x05
#define PENDING_TOKEN_INHIBIT 0x06
#define PENDING_WELCOME 0x07
#define GETTING_OMC 0x08
#define DOMAIN_READY 0x09
//
//
// SHARED MEMORY STRUCTURE
//
// This structure holds various private (to ObMan) state information.
//
// The ObMan task allocates and initialises one instance of this structure
// when it initialises; it resides at the base of the OMGLOBAL memory
// block.
//
// It is discarded when the ObMan task terminates.
//
//
typedef struct tagOM_PRIMARY
{
STRUCTURE_STAMP
PUT_CLIENT putTask;
PMG_CLIENT pmgClient; // OM's network layer handle
PCM_CLIENT pcmClient; // OM's Call Manager handle
BASEDLIST domains; // Domains
OM_CLIENT clients[OMCLI_MAX]; // Secondaries
UINT objectIDsequence;
BOOL exitProcReg:1;
BOOL eventProcReg:1;
OM_CORRELATOR correlator;
WORD pad1;
LPBYTE pgdcWorkBuf;
BYTE compressBuffer[OM_NET_SEND_POOL_SIZE / 2];
}
OM_PRIMARY;
void __inline ValidateOMP(POM_PRIMARY pomPrimary)
{
ASSERT(!IsBadWritePtr(pomPrimary, sizeof(OM_PRIMARY)));
}
void __inline ValidateOMS(POM_CLIENT pomClient)
{
extern POM_PRIMARY g_pomPrimary;
ValidateOMP(g_pomPrimary);
ASSERT(!IsBadWritePtr(pomClient, sizeof(OM_CLIENT)));
ASSERT(pomClient < &(g_pomPrimary->clients[OMCLI_MAX]));
ASSERT(pomClient >= &(g_pomPrimary->clients[OMCLI_FIRST]));
}
//
//
// Workset group registration/move request control block
//
// This structure is used to pass the parameters of a workset group
// registration/move request to the ObMan task (from a Client task).
//
// Not all fields are used by both the registration and the move process.
//
// The <type> field is used to distinguish between a WSGroupMove and a
// WSGroupRegister.
//
//
typedef struct tagOM_WSGROUP_REG_CB
{
STRUCTURE_STAMP
BASEDLIST chain;
PUT_CLIENT putTask;
UINT callID;
OMWSG wsg;
OMFP fpHandler;
OM_CORRELATOR correlator;
OM_CORRELATOR lockCorrelator;
OM_CORRELATOR channelCorrelator;
WORD retryCount;
POM_USAGE_REC pUsageRec;
POM_WSGROUP pWSGroup;
POM_DOMAIN pDomain; // ObMan pointer to Domain record
BOOL valid;
OM_WSGROUP_HANDLE hWSGroup;
BYTE type; // REGISTER or MOVE
BYTE mode; // PRIMARY or SECONDARY
BYTE flags; // see below
}
OM_WSGROUP_REG_CB;
typedef OM_WSGROUP_REG_CB * POM_WSGROUP_REG_CB;
//
// Values for the <type> field:
//
#define WSGROUP_MOVE 0x01
#define WSGROUP_REGISTER 0x02
//
// Flags for the <flags> field:
//
#define BUMPED_CBS 0x0001 // indicates whether we bumped use
// counts of pWSGroup, pDomain
#define LOCKED_OMC 0x0002 // indicates whether we've locked
// ObManControl
//
// Values for the <mode> field (we use the flag macro because the values
// may be ORed together and so need to be bit-independent):
//
#define PRIMARY 0x0001
#define SECONDARY 0x0002
//
//
// HELPER CONTROL BLOCK
//
// When we receive a WSG_SEND_REQUEST from a remote node, we checkpoint the
// workset group requested. This is an asynchronous process (it's
// essentially getting a lock on a dummy workset), so we need to store the
// details of the remote node away somewhere. We do this using a help CB
// with the following structure:
//
//
typedef struct tagOM_HELPER_CB
{
STRUCTURE_STAMP
BASEDLIST chain;
NET_UID lateJoiner; // MCS user ID of late joiner
OM_CORRELATOR remoteCorrelator;
POM_WSGROUP pWSGroup; // pWSGroup is bumped during
// checkpoint
OM_CORRELATOR lockCorrelator; // returned by WorksetLockReq
// and recd in WORKSET_LOCK_CON
WORD pad1;
}
OM_HELPER_CB;
typedef OM_HELPER_CB * POM_HELPER_CB;
//
//
// THE SEND QUEUE
//
// For each Domain, and for each network priority, ObMan maintains a queue
// of message and data to be sent to the network. Clients, when executing
// API functions, cause instructions to be added to the tail of one of
// these queues.
//
// The ObMan task, in response to an OMINT_EVENT_SEND_QUEUE event, processes as
// many send queue operations as possible, giving up for a while when it
// runs out of network buffers.
//
// This is subject to the restriction that no operations are processed from
// one send queue when operations exist on a queue of higher priority in
// the same Domain.
//
// Instructions on the send queue have the following format:
//
//
typedef struct tagOM_SEND_INST
{
STRUCTURE_STAMP
BASEDLIST chain;
UINT callID; // the relevant Domain
NET_CHANNEL_ID channel; // the channel to send the event on
NET_PRIORITY priority; // priority to send event on
POM_WSGROUP pWSGroup;
POM_WORKSET pWorkset;
POMNET_PKT_HEADER pMessage;
POM_OBJECT pObj;
POM_OBJECTDATA pDataStart;
POM_OBJECTDATA pDataNext;
WORD messageSize; // length of message at pMessage
OMNET_MESSAGE_TYPE messageType; // == OMNET_OBJECT_ADD, etc.
UINT dataLeftToGo; // number of bytes of data left to
// be sent
UINT compressOrNot; // Some packets are never compressed
}
OM_SEND_INST;
typedef OM_SEND_INST * POM_SEND_INST;
//
//
// RECEIVE LIST
//
// ObMan maintains a list of structures holding information about data
// transfers (receives) which have begun but not finished. This is known
// as the receive list.
//
// When ObMan receives a header packet for an Add, Update or Replace
// operation, it adds an entry to the receive list. Subsequent data
// packets are then correlated with this entry, until the entire object has
// been received, at which point the Add/Update/Replace operation is
// carried out.
//
// The receive list is a linked list of entries with the following format:
//
//
typedef struct tagOM_RECEIVE_CB
{
STRUCTURE_STAMP
BASEDLIST chain;
POM_DOMAIN pDomain; // Domain record pointer
POMNET_OPERATION_PKT pHeader; // ObMan pointer to message header
void * pData; // ObMan pointer to the data that
// is being transferred
UINT bytesRecd; // total bytes received so far for
// this transfer
LPBYTE pCurrentPosition; // points to where next chunk
// of data should be copied
NET_PRIORITY priority; // priority of data transfer
NET_CHANNEL_ID channel;
}
OM_RECEIVE_CB;
typedef OM_RECEIVE_CB * POM_RECEIVE_CB;
//
// HANDLE <--> PTR CONVERSION ROUTINES
// Object, usage, domain, workset group, worksets
//
POM_WSGROUP __inline GetOMCWsgroup(POM_DOMAIN pDomain)
{
POM_WSGROUP pWSGroup;
pWSGroup = (POM_WSGROUP)COM_BasedListFirst(&(pDomain->wsGroups),
FIELD_OFFSET(OM_WSGROUP, chain));
ValidateWSGroup(pWSGroup);
return(pWSGroup);
}
POM_WORKSET __inline GetOMCWorkset(POM_DOMAIN pDomain, OM_WORKSET_ID worksetID)
{
POM_WSGROUP pWSGroup;
pWSGroup = GetOMCWsgroup(pDomain);
return(pWSGroup->apWorksets[worksetID]);
}
OM_CORRELATOR __inline NextCorrelator(POM_PRIMARY pomPrimary)
{
return(pomPrimary->correlator++);
}
void __inline UpdateWorksetGeneration(POM_WORKSET pWorkset, POMNET_OPERATION_PKT pPacket)
{
pWorkset->genNumber = max(pWorkset->genNumber, pPacket->seqStamp.genNumber) + 1;
}
//
//
// CHECK_WORKSET_NOT_EXHAUSTED(pWorkset)
//
// This macro checks that the specified workset is not exhausted. If it
// is, it calls DC_QUIT.
//
//
#define CHECK_WORKSET_NOT_EXHAUSTED(pWorkset) \
\
if (pWorkset->genNumber == OM_MAX_GENERATION_NUMBER) \
{ \
WARNING_OUT(("Workset %hx exhausted", pWorkset->worksetID)); \
rc = OM_RC_WORKSET_EXHAUSTED; \
DC_QUIT; \
}
//
//
// CHECK_WORKSET_NOT_LOCKED(pWorkset)
//
// This macro checks that the specified workset is not locked. If it is,
// it calls DC_QUIT with an error.
//
//
#define CHECK_WORKSET_NOT_LOCKED(pWorkset) \
\
if (pWorkset->lockState == LOCK_GRANTED) \
{ \
rc = OM_RC_WORKSET_LOCKED; \
WARNING_OUT(("Workset %hx locked - can't proceed", worksetID)); \
DC_QUIT; \
}
//
//
// OBJECT ID MANIPULATION
//
// These macros manipulate object IDs.
//
//
//
//
// OBJECT_ID_IS_NULL(objectID)
//
// This macro evaluates to TRUE if the supplied object ID is a null ID,
// and FALSE otherwise.
//
//
#define OBJECT_ID_IS_NULL(objectID) \
\
(((objectID).creator == 0) && ((objectID).sequence == 0))
//
//
// GET_NEXT_OBJECT_ID(objectID, pDomain, pWorkset)
//
// This macro allocates a new object ID for the workset specified by
// <pWorkset>. It copies the ID into the structure specified by
// <objectID>.
//
// The first field in the ID is ObMan's MCS user ID in the Domain to which
// the workset group <pWSGroup> belongs.
//
//
#define GET_NEXT_OBJECT_ID(objectID, pDomain, pomPrimary) \
(objectID).creator = pDomain->userID; \
(objectID).sequence = pomPrimary->objectIDsequence++; \
(objectID).pad1 = 0
//
//
// OBJECT_IDS_ARE_EQUAL(objectID1, objectID2)
//
// Evaluates to TRUE if the two object IDs are equal and FALSE otherwise.
//
//
#define OBJECT_IDS_ARE_EQUAL(objectID1, objectID2) \
\
(memcmp(&(objectID1), &(objectID2), sizeof(OM_OBJECT_ID)) == 0)
//
//
// SEQUENCE STAMP MANIPULATION
//
// These macro manipulate sequence stamps.
//
//
//
//
// GET_CURR_SEQ_STAMP(stamp, pWSGroup, pWorkset)
//
// This macro copies the current sequence stamp of the workset specified by
// <pWorkset> into the sequence stamp structure identified by <stamp>.
//
//
#define GET_CURR_SEQ_STAMP(stamp, pDomain, pWorkset) \
\
(stamp).userID = pDomain->userID; \
(stamp).genNumber = pWorkset->genNumber
//
// GenerateMessage(...)
//
// Allocates and initialises an OMNET_OPERATION_PKT of the specified type.
// Note that the <size> field is set to zero even if <messageType> is an
// add, update or replace. The QueueMessage function will set the size to
// the correct value when the message is queued.
//
UINT GenerateOpMessage( POM_WSGROUP pWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT_ID pObjectID,
POM_OBJECTDATA pData,
OMNET_MESSAGE_TYPE messageType,
POMNET_OPERATION_PKT * ppPacket);
//
//
// QueueMessage(...)
//
// This function creates a send instruction for the specified message and
// places the instruction on the specified send queue for the specified
// Domain. It them sends an event to ObMan prompting it to examine the
// queue.
//
//
UINT QueueMessage(PUT_CLIENT putTask,
POM_DOMAIN pDomain,
NET_CHANNEL_ID channelID,
NET_PRIORITY priority,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_OBJECT pObjectRec,
POMNET_PKT_HEADER pPacket,
POM_OBJECTDATA pData,
BOOL compressOrNot);
//
// GetMessageSize(...)
//
UINT GetMessageSize(OMNET_MESSAGE_TYPE messageType);
//
// PreProcessMessage(...)
//
UINT PreProcessMessage(POM_DOMAIN pDomain,
OM_WSGROUP_ID wsGroupID,
OM_WORKSET_ID worksetID,
POM_OBJECT_ID pObjectID,
OMNET_MESSAGE_TYPE messageType,
POM_WSGROUP * ppWSGroup,
POM_WORKSET * ppWorkset,
POM_OBJECT * ppObjectRec);
//
//
// PurgeNonPersistent(...)
//
// Purges any objects added by <userID> from non-persistent worksets in the
// workset group identified by <wsGroupID> in the specified domain.
//
//
void PurgeNonPersistent(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OM_WSGROUP_ID wsGroupID,
NET_UID userID);
//
// ProcessWorksetNew(...)
//
UINT ProcessWorksetNew(PUT_CLIENT putTask,
POMNET_OPERATION_PKT pPacket,
POM_WSGROUP pWSGroup);
//
// ProcessWorksetClear(...)
//
UINT ProcessWorksetClear(PUT_CLIENT putTask, POM_PRIMARY pomPrimary,
POMNET_OPERATION_PKT pPacket,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset);
//
// ProcessObjectAdd(...)
//
UINT ProcessObjectAdd(PUT_CLIENT putTask,
POMNET_OPERATION_PKT pPacket,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_OBJECTDATA pData,
POM_OBJECT * ppObj);
//
// ProcessObjectMove(...)
//
void ProcessObjectMove(PUT_CLIENT putTask,
POMNET_OPERATION_PKT pPacket,
POM_WORKSET pWorkset,
POM_OBJECT pObjectRec);
//
// ProcessObjectDRU(...)
//
UINT ProcessObjectDRU(PUT_CLIENT putTask,
POMNET_OPERATION_PKT pPacket,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_OBJECT pObj,
POM_OBJECTDATA pData);
//
// ObjectAdd(...)
//
UINT ObjectAdd(PUT_CLIENT putTask, POM_PRIMARY pomPrimary,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_OBJECTDATA pData,
UINT updateSize,
OM_POSITION position,
OM_OBJECT_ID * pObjectID,
POM_OBJECT * ppObj);
//
// WSGroupEventPost(...)
//
// This function posts the specified event to all local Clients registered
// with the workset group. The <param2> parameter is the second parameter
// on the event to be posted.
//
//
UINT WSGroupEventPost(PUT_CLIENT putTaskFrom,
POM_WSGROUP pWSGroup,
BYTE target,
UINT event,
OM_WORKSET_ID worksetID,
UINT_PTR param2);
//
//
// This function is called by
//
// - OM_WorksetOpen, when a Client creates a new workset
//
// - ProcessLockRequest, when a lock request arrives for a workset we
// don't yet know about
//
// - xx, when an OMNET_WORKSET_NEW message arrives.
//
// It creates the local data structures for the workset and posts an event
// to all local Clients registered with the workset group.
//
//
//
//
// WorksetCreate(...)
//
// This function creates a new workset in the specified workset group.
//
// It calls GenerateMessage, ProcessWorksetNew and QueueMessage.
//
//
UINT WorksetCreate(PUT_CLIENT putTask,
POM_WSGROUP pWSGroup,
OM_WORKSET_ID worksetID,
BOOL fTemp,
NET_PRIORITY priority);
//
//
// WorksetEventPost(...)
//
// This function posts the specified event to all local Clients which have
// the workset open (at most 1 Client in R1.1).
//
// The <putTask> parameter is the putTask of the invoking task (and NOT
// the handle of the task to post the event to).
//
// The number of Clients the event was successfully posted to is returned
// in *pNumPosts, if pNumPosts is not NULL. A caller which wishes to
// ignore the number of events posted can pass in NULL as the pNumPosts
// parameter.
//
//
UINT WorksetEventPost(PUT_CLIENT putTask,
POM_WORKSET pWorkset,
BYTE target,
UINT event,
POM_OBJECT pObj);
//
// WorksetDoClear(...)
//
void WorksetDoClear(PUT_CLIENT putTask,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_PENDING_OP pPendingOp);
//
//
// ProcessLockRequest(...)
//
// This function is called when ObMan receives an OMNET_LOCK_REQ message
// from another node.
//
// If we
//
// - have the workset locked already, or
//
// - are trying to lock the workset and our MCS user ID is greater than the
// node which sent us the request,
//
// we deny the lock (i.e. send back a negative OMNET_LOCK_REPLY).
//
// In all other cases, we grant the lock (i.e. send back an affirmative
// OMNET_LOCK_REPLY).
//
// If we grant the lock to the remote node when we were trying to get it
// for ourselves, our attempt to lock the workset has failed so we call
// WorksetLockResult with a failure code.
//
//
void ProcessLockRequest(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POMNET_LOCK_PKT pLockReqPkt);
//
//
// QueueLockReply(...)
//
// This function is called when we have decided to grant or deny a lock
// request received from another node. It queues the appropriate response
// on ObMan's send queue.
//
//
void QueueLockReply(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OMNET_MESSAGE_TYPE result,
NET_CHANNEL_ID destination,
POMNET_LOCK_PKT pLockReqPkt);
//
//
// QueueLockNotify(...)
//
// Queues a LOCK_NOTIFY command on the broadcast channel for the workset
// group, indicating that we have granted the lock to the <locker>.
//
//
void QueueLockNotify(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
NET_UID locker);
//
//
// ProcessLockReply(...)
//
// This function is called when ObMan receives an OMNET_LOCK_GRANT or
// OMNET_LOCK_DENY message from another node, in response to an
// OMNET_LOCK_REQ message we sent out earlier.
//
// The function removes this node from the list of expected respondents for
// this lock (if it is in the list).
//
// If the list is now empty, the lock has succeeded so WorksetLockResult is
// called.
//
// Otherwise, nothing else happens for the moment.
//
//
void ProcessLockReply(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
NET_UID sender,
OM_CORRELATOR correlator,
OMNET_MESSAGE_TYPE replyType);
//
// PurgeLockRequests(...)
//
void PurgeLockRequests(POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup);
//
//
// ProcessLockTimeout(...)
//
// This function is called when ObMan receives a lock timeout event. It
// checks to see if any of the nodes from whom we are still expecting lock
// replies have in fact gone away; if they have, it removes them from the
// list of expected respondents.
//
// If this list is now empty, the lock has succeeded and WorksetLockResult
// is called.
//
// If the list is not empty, then another delayed lock timeout event is
// posted to ObMan, unless we have already had the maximum number of
// timeouts for this lock, in which case the lock has failed and
// WorksetLockResult is called.
//
//
void ProcessLockTimeout(POM_PRIMARY pomPrimary,
UINT retriesToGo,
UINT callID);
//
//
// WorksetLockReq(...)
//
// This function is called
//
// - by OM_WorksetLockReq, when a Client wants to lock a workset
//
// - by LockObManControl, when ObMan wants to lock workset #0 in
// ObManControl.
//
// The function decides whether the lock can be granted or refused
// synchronously, and if so calls WorksetLockResult. If not, it posts an
// OMINT_EVENT_LOCK_REQ event to the ObMan task, which results in the
// ProcessLocalLockRequest function being called later.
//
// The <hWSGroup> parameter is the workset group handle which will be
// included in the eventual OM_WORKSET_LOCK_CON event. Its value is not
// used in the function; when this function is called in the ObMan task
// this value is set to zero (since the ObMan task doesn't use workset
// group handles).
//
// On successful completion, the <pCorrelator> parameter points to the
// correlator value which will be included in the eventual
// OM_WORKSET_LOCK_CON event.
//
//
void WorksetLockReq(PUT_CLIENT putTask, POM_PRIMARY pomPrimary,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
OM_WSGROUP_HANDLE hWSGroup,
OM_CORRELATOR * pCorrelator);
//
//
// WorksetLockResult(...)
//
// This function is called when we have finished processing a request to
// obtain a workset lock. The function sets the workset state accordingly,
// posts an appropriate event to the task which requested the lock, and
// frees the lock request control block.
//
//
void WorksetLockResult(PUT_CLIENT putTask,
POM_LOCK_REQ * ppLockReq,
UINT result);
//
//
// BuildNodeList(...)
//
// This function builds a list of the remote nodes which are registered
// with the workset group referenced in the lock request CB passed in.
//
//
UINT BuildNodeList(POM_DOMAIN pDomain, POM_LOCK_REQ pLockReq);
//
//
// HandleMultLockReq(...)
//
// This function searches the global list of pending lock requests (stored
// in the root data structure) for any lock requests matching the Domain,
// workset group and workset specified.
//
//
void HandleMultLockReq(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
UINT result);
//
//
// FindLockReq(...)
//
// This function searches the global list of pending lock requests (stored
// in the root data structure) for a lock request that matches the Domain,
// workset group and workset specified.
//
// If found, a pointer to the lock request is returned in <ppLockReq>.
//
// It can search for a primary lock request if needed
//
//
void FindLockReq(POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_LOCK_REQ * ppLockreq,
BYTE lockType);
//
// ReleaseAllNetLocks(...)
//
void ReleaseAllNetLocks(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OM_WSGROUP_ID wsGroupID,
NET_UID userID);
//
//
// ProcessUnlock(...)
//
// This function is called when an OMNET_UNLOCK message is received from
// the network. The function is a wrapper which just derives a workset
// pointer and calls ProcessUnlock (above).
//
//
void ProcessUnlock(POM_PRIMARY pomPrimary,
POM_WORKSET pWorkset,
NET_UID sender);
//
// WorksetUnlock(...)
//
void WorksetUnlock(PUT_CLIENT putTask, POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset);
//
// WorksetUnlockLocal(...)
//
void WorksetUnlockLocal(PUT_CLIENT putTask, POM_WORKSET pWorkset);
//
//
// ObjectDoDelete(...)
//
// This function deletes an object in a workset. It is called by
//
// - OM_ObjectDeleteConfirm, when a Client confirms the deletion of an
// object.
//
// - WorksetDoClear, to delete each individual object
//
// - ProcessObjectDelete when ObMan receives a Delete message from the
// network for an object in a workset which no local Clients have open.
//
//
void ObjectDoDelete(PUT_CLIENT putTask,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_OBJECT pObj,
POM_PENDING_OP pPendingOp);
//
//
// ObjectDRU(...)
//
// This function generate, processes and queues a message of type DELETE,
// REPLACE or UPDATE (as specified by <type>).
//
//
UINT ObjectDRU(PUT_CLIENT putTask,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_OBJECT pObj,
POM_OBJECTDATA pData,
OMNET_MESSAGE_TYPE type);
//
//
// ObjectRead(...)
//
// This function converts an object handle to a pointer to the object data.
// An invalid handle causes an assertion failure.
//
//
void ObjectRead(POM_CLIENT pomClient,
POM_OBJECT pObj,
POM_OBJECTDATA * ppData);
//
// ObjectInsert(...)
//
void ObjectInsert(POM_WORKSET pWorkset,
POM_OBJECT pObj,
OM_POSITION position);
//
// ObjectDoMove(...)
//
void ObjectDoMove(POM_OBJECT pObjToMove,
POM_OBJECT pOtherObjectRec,
OM_POSITION position);
//
// ObjectDoUpdate(...)
//
void ObjectDoUpdate(PUT_CLIENT putTask,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_OBJECT pObj,
POM_PENDING_OP pPendingOp);
//
// ObjectDoReplace(...)
//
void ObjectDoReplace(PUT_CLIENT putTask,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_OBJECT pObj,
POM_PENDING_OP pPendingOp);
//
// ObjectIDToPtr(...)
//
UINT ObjectIDToPtr(POM_WORKSET pWorkset,
OM_OBJECT_ID objectID,
POM_OBJECT * ppObj);
//
// FindPendingOp(...)
//
void FindPendingOp(POM_WORKSET pWorkset,
POM_OBJECT pObj,
OM_OPERATION_TYPE type,
POM_PENDING_OP * ppPendingOp);
//
// WSGRecordFind(...)
//
void WSGRecordFind(POM_DOMAIN pDomain, OMWSG wsg, OMFP fpHandler,
POM_WSGROUP * ppWSGroup);
//
// DeterminePriority(...)
//
void DeterminePriority(NET_PRIORITY * pPriority,
POM_OBJECTDATA pData);
//
// RemoveClientFromWSGList(...)
//
// The second parameter is the putTask of the Client to be deregistered.
// It is NOT (well, not necessarily) the putTask of the calling task, and
// for this reason (to avoid it being used as such) is passed as a 32-bit
// integer.
//
void RemoveClientFromWSGList(
PUT_CLIENT putUs,
PUT_CLIENT putTask,
POM_WSGROUP pWSGroup);
//
// AddClientToWSGList(...)
//
UINT AddClientToWSGList(PUT_CLIENT putTask,
POM_WSGROUP pWSGroup,
OM_WSGROUP_HANDLE hWSGroup,
UINT mode);
//
// AddClientToWsetList(...)
//
UINT AddClientToWsetList(PUT_CLIENT putTask,
POM_WORKSET pWorkset,
OM_WSGROUP_HANDLE hWSGroup,
UINT mode,
POM_CLIENT_LIST * pClientListEntry);
//
// PostWorksetNewEvents(...)
//
UINT PostWorksetNewEvents(PUT_CLIENT putFrom,
PUT_CLIENT putTask,
POM_WSGROUP pWSGroup,
OM_WSGROUP_HANDLE hWSGroup);
//
//
// QueueUnlock(...)
//
// This function queues a workset unlock packet for sending to the
// specified destination.
//
//
UINT QueueUnlock(PUT_CLIENT putTask,
POM_DOMAIN pDomain,
OM_WSGROUP_ID wsGroupID,
OM_WORKSET_ID worksetID,
NET_UID destination,
NET_PRIORITY priority);
//
// PurgeReceiveCBs(...)
//
void PurgeReceiveCBs(POM_DOMAIN pDomain,
NET_CHANNEL_ID channel);
//
// FreeSendInst()
//
void FreeSendInst(POM_SEND_INST pSendInst);
//
// SetPersonData(...)
//
UINT SetPersonData(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup);
//
//
// FindInfoObject(...)
//
// This function searches workset #0 in the ObManControl workset group in
// the specified Domain for a matching info object.
//
// The match is performed as follows:
//
// - if functionProfile and wsGroupName are not NULL, the first object
// matching both is returned
//
// - if functionProfile is not NULL but wsGroupName is, the first object
// matching functionProfile is returned
//
// - if functionProfile is NULL, the first object matching wsGroupID is
// returned.
//
//
void FindInfoObject(POM_DOMAIN pDomain,
OM_WSGROUP_ID wsGroupID,
OMWSG wsg,
OMFP fpHandler,
POM_OBJECT * ppInfoObjectRec);
//
//
// FindPersonObject(...)
//
// This function searches the specified workset in ObManControl looking for
// a registration object which has
//
// - the same user ID as <userID>, if <searchType> == FIND_THIS
//
// - a different user ID from <userID>, if <searchType> == FIND_OTHERS.
//
//
void FindPersonObject(POM_WORKSET pOMCWorkset,
NET_UID userID,
UINT searchType,
POM_OBJECT * ppRegObjectRec);
#define FIND_THIS 1
#define FIND_OTHERS 2
//
// ProcessOMCWorksetNew(...)
//
void ProcessOMCWorksetNew(POM_PRIMARY pomPrimary, OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID);
//
// ProcessOMCObjectEvents(...)
//
void ProcessOMCObjectEvents(POM_PRIMARY pomPrimary,
UINT event,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj);
//
// GeneratePersonEvents(...)
//
void GeneratePersonEvents(POM_PRIMARY pomPrimary,
UINT event,
POM_WSGROUP pWSGroup,
POM_OBJECT pObj);
//
// PostAddEvents(...)
//
UINT PostAddEvents(PUT_CLIENT putTaskFrom,
POM_WORKSET pWorkset,
OM_WSGROUP_HANDLE hWSGroup,
PUT_CLIENT putTaskTo);
//
// RemovePersonObject(...)
//
void RemovePersonObject(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OM_WSGROUP_ID wsGroupID,
NET_UID detachedUserID);
//
// RemoveInfoObject(...)
//
void RemoveInfoObject(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain,
OM_WSGROUP_ID wsGroupID);
//
//
// DEBUG ONLY FUNCTIONS
//
// These functions are debug code only - for normal compilations, the
// declarations are #defined to nothing and the definitions are
// preprocessed out altogether.
//
//
#ifndef _DEBUG
#define CheckObjectCount(x, y)
#define CheckObjectOrder(x)
#define DumpWorkset(x, y)
#else // _DEBUG
//
//
// CheckConstants(...)
//
// The ObMan code relies on certain assumptions about the sizes and formats
// of various data structures, and the values of certain constants.
//
//
// The OMNET_OPERATION_PKT type has two one-byte fields, <position> and
// <flags>, which are used to hold
//
// - a NET_PRIORITY value which indicates the priority for the
// workset for WORKSET_NEW/WORKSET_CATCHUP messages, and
//
// - a UINT (the number of bytes being acknowledged) in the case
// of a DATA_ACK message.
//
// GenerateOpMessage and AckData cast the <position> field to a two-byte
// quantity for this purpose. Therefore, it is necessary that these two
// fields exist, that they are adjacent and that the <position> one
// comes first.
//
// In addition, since the priority information is a NET_PRIORITY, we
// must ensure that a NET_PRIORITY is indeed two bytes long.
//
//
// ASSERT((sizeof(NET_PRIORITY) == (2 * sizeof(BYTE))));
//
// ASSERT((offsetof(OMNET_OPERATION_PKT, position) + 1 ==
// offsetof(OMNET_OPERATION_PKT, flags)));
//
//
// In many places, for-loops use workset IDs as the loop variable and
// OM_MAX_WORKSETS_PER_WSGROUP as the end condition. To avoid infinite
// loops, this constant must be less than 256:
//
// ASSERT((OM_MAX_WORKSETS_PER_WSGROUP < 256));
//
// The OMC WSG has one workset for each WSG in the Domain. Since the
// number of worksets per WSG is limited, the # of WSGs per Domain is
// limited in the same way:
//
// ASSERT(OM_MAX_WSGROUPS_PER_DOMAIN <= OM_MAX_WORKSETS_PER_WSGROUP);
//
//
//
// CheckObjectCount(...)
//
// This function counts the number of non-deleted objects in the specified
// workset and compares this against the <numObjects> field of the workset
// record. A mismatch causes an assertion failure.
//
//
void CheckObjectCount(POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset);
//
// CheckObjectOrder(...)
//
void CheckObjectOrder(POM_WORKSET pWorkset);
#endif // _DEBUG
//
//
// WORKSET OPEN/CLOSE BITFIELD MANIPULATION MACROS
//
// ObMan maintains one usage record for each workset group a Client is
// registered with. One of the fields of the usage record is an 32-byte
// bitfield which is interpreted as an array of 256 booleans, indicating
// whether a Client has the corresponding workset open.
//
// These macros use the EXTRACT_BIT, SET_BIT and CLEAR_BIT macros to set and clear the bit
// for <worksetID> in the <worksetOpenFlags> bitfield of the usage record.
//
//
BOOL __inline WORKSET_IS_OPEN(POM_USAGE_REC pUsageRec, OM_WORKSET_ID worksetID)
{
return((pUsageRec->worksetOpenFlags[worksetID / 8] & (0x80 >> (worksetID % 8))) != 0);
}
void __inline WORKSET_SET_OPEN(POM_USAGE_REC pUsageRec, OM_WORKSET_ID worksetID)
{
pUsageRec->worksetOpenFlags[worksetID / 8] |= (0x80 >> (worksetID % 8));
}
void __inline WORKSET_SET_CLOSED(POM_USAGE_REC pUsageRec, OM_WORKSET_ID worksetID)
{
pUsageRec->worksetOpenFlags[worksetID / 8] &= ~(0x80 >> (worksetID % 8));
}
//
//
// ReleaseAllLocks(...)
//
// This function releases all the locks held by a particular Client for a
// particular workset. In R1.1, this is at most one lock (the workset
// lock) but if/when object locking is supported, this function will also
// release all object locks held.
//
// This function is closed when a Client is closing a workset.
//
//
void ReleaseAllLocks(POM_CLIENT pomClient,
POM_USAGE_REC pUsageRec,
POM_WORKSET pWorkset);
//
//
// ReleaseAllObjects(...)
//
// This function releases all the objects held by a particular Client in a
// particular workset.
//
// This function is called when a Client closes a workset.
//
void ReleaseAllObjects(POM_USAGE_REC pUsageRec, POM_WORKSET pWorkset);
//
//
// ConfirmAll(...)
//
// This function confirms any pending operations for the workset specified.
//
// The function is called when a Client closes a workset.
//
// Since this function may call WorksetDoClear, the caller must hold the
// workset group mutex.
//
//
void ConfirmAll(POM_CLIENT pomClient,
POM_USAGE_REC pUsageRec,
POM_WORKSET pWorkset);
//
//
// DiscardAllObjects(...)
//
// This function discards any objects allocated for the specified Client
// for the specified workset but so far unused.
//
// The function is called when a Client closes a workset.
//
//
void DiscardAllObjects(POM_USAGE_REC pUsageRec,
POM_WORKSET pWorkset);
//
//
// RemoveFromUnusedList
//
// This function removes an object (specified by a pointer to the object)
// from the Client's list of unused objects. It is called by
//
// - OM_ObjectAdd, OM_ObjectUpdate and OM_ObjectReplace when a
// Client inserts an object into a workset, or
//
// - OM_ObjectDiscard, when a Client discards an unused object.
//
//
void RemoveFromUnusedList(POM_USAGE_REC pUsageRec, POM_OBJECTDATA pData);
//
//
// OM_ObjectAdd(...)
//
// This function adds an object to a worksets, in the specified position.
//
// Although it is not strictly an API function, it performs full parameter
// validation and could be externalised easily.
//
//
UINT OM_ObjectAdd(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECTDATA * ppData,
UINT updateSize,
POM_OBJECT * ppObj,
OM_POSITION position);
//
//
// OM_ObjectMove(...)
//
// This function moves an object to the start or end of a workset. It is
// called by OM_ObjectMoveFirst and OM_ObjectMoveLast.
//
// Although it is not strictly an API function, it performs full parameter
// validation and could be externalised easily.
//
//
UINT OM_ObjectMove(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj,
OM_POSITION position);
//
//
// ValidateParamsX(...)
//
// These functions are used to validate parameters and convert them to
// various pointers, as follows:
//
// ValidateParams2 - checks pomClient, hWSGroup
// - returns pUsageRec, pWSGroup
//
// ValidateParams3 - checks pomClient, hWSGroup, worksetID,
// - returns pUsageRec, pWorkset
//
// Note: also asserts that workset is open
//
// ValidateParams4 - checks pomClient, hWSGroup, worksetID, pObj
//
// Each of the functions uses DCASSERT to bring down the calling task if an
// invalid parameter is detected.
//
//
__inline void ValidateParams2(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
UINT requiredMode,
POM_USAGE_REC * ppUsageRec,
POM_WSGROUP * ppWSGroup)
{
ValidateOMS(pomClient);
ASSERT(ValidWSGroupHandle(pomClient, hWSGroup));
*ppUsageRec = pomClient->apUsageRecs[hWSGroup];
ValidateUsageRec(*ppUsageRec);
ASSERT(requiredMode & (*ppUsageRec)->mode);
*ppWSGroup = (*ppUsageRec)->pWSGroup;
ValidateWSGroup(*ppWSGroup);
}
__inline void ValidateParams3(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
UINT requiredMode,
POM_USAGE_REC * ppUsageRec,
POM_WORKSET * ppWorkset)
{
POM_WSGROUP pWSGroup;
ValidateParams2(pomClient, hWSGroup, requiredMode, ppUsageRec, &pWSGroup);
ASSERT(WORKSET_IS_OPEN(*ppUsageRec, worksetID));
*ppWorkset = pWSGroup->apWorksets[worksetID];
ValidateWorkset(*ppWorkset);
}
__inline void ValidateParams4(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj,
UINT requiredMode,
POM_USAGE_REC * ppUsageRec,
POM_WORKSET * ppWorkset)
{
ValidateParams3(pomClient, hWSGroup, worksetID, requiredMode, ppUsageRec,
ppWorkset);
ValidateObject(pObj);
ASSERT(!(pObj->flags & DELETED));
}
//
//
// SetUpUsageRecord(...)
//
UINT SetUpUsageRecord(POM_CLIENT pomClient,
UINT mode,
POM_USAGE_REC* ppUsageRec,
OM_WSGROUP_HANDLE * phWSGroup);
//
// FindUnusedWSGHandle()
//
UINT FindUnusedWSGHandle(POM_CLIENT pomClient, OM_WSGROUP_HANDLE * phWSGroup);
//
//
// ObjectRelease(...)
//
// This function releases the specified Client's hold on the the specified
// object and removes the relevant entry from the Client's objects-in-use
// list.
//
// If <pObj> is NULL, the function releases the first object held by
// this Client in the specified workset, if any. If there are none, the
// function returns OM_RC_OBJECT_NOT_FOUND.
//
//
UINT ObjectRelease(POM_USAGE_REC pUsageRec,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj);
//
//
// WorksetClearPending(...)
//
// Look for a CLEAR_IND which is outstanding for the given workset which,
// when confirmed, will cause the given object to be deleted.
//
// Returns TRUE if such a CLEAR_IND is outstanding, FALSE otherwise.
//
//
BOOL WorksetClearPending(POM_WORKSET pWorkset, POM_OBJECT pObj);
UINT OM_Register(PUT_CLIENT putTask, OMCLI omClient, POM_CLIENT * ppomClient);
//
//
// Description:
//
// This function registers a DC-Groupware task as an ObMan Client. A task
// must be a registered ObMan Client in order to call any of the other API
// functions.
//
// On successful completion, the value at <ppomClient> is this Client's ObMan
// handle, which must be passed as a parameter to all other API functions.
//
// This function registers an event handler and an exit procedure for the
// Client, so Clients must have previously registered fewer than the maximum
// number of Utility Service event handlers and exit procedures.
//
// If the are too many Clients already registered with ObMan, an error is
// returned.
//
// Ensuing Events:
//
// None
//
// Return Codes
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_TOO_MANY_CLIENTS
//
//
void OM_Deregister(POM_CLIENT * ppomCient);
void CALLBACK OMSExitProc(LPVOID pomClient);
//
//
// Description:
//
// This function deregisters an ObMan Client.
//
// On completion, the ObMan handle which the Client was using becomes
// invalid and the value at <ppomClient> is set to NULL to prevent the task
// from using it again.
//
// This function deregisters the Client from any workset groups with which
// it was registered.
//
// Ensuing Events:
//
// None
//
// Return Codes
//
// None
//
//
UINT OM_WSGroupRegisterPReq(POM_CLIENT pomClient,
UINT call,
OMFP fpHandler,
OMWSG wsg,
OM_CORRELATOR * pCorrelator);
//
//
// Description:
//
// This is an asynchronous function which requests that ObMan register a
// Client with a particular workset group for PRIMARY access. The workset
// group is determined by the following:
//
// - <call> is the DC-Groupware Call which contains/is to contain the
// workset group (or OM_NO_CALL if the workset group is/is to
// be a local workset group)
//
// - <functionProfile> is the Function Profile for the workset group
//
// - <wsGroupName> is the name of the workset group.
//
// The <pomClient> parameter is the ObMan handle returned by the OM_Register
// function.
//
// If a Client wishes to create a new, or register with an existing, workset
// group which exists only on the local machine, the value OM_NO_CALL should
// be specified for the <call> parameter. Workset groups created in this
// way for purely local use may be subsequently transferred into a call by
// invoking OM_WSGroupMoveReq at some later time.
//
// If this function completes successfully, the Client will subsequently
// receive an OM_WSGROUP_REGISTER_CON event indicating the success or
// failure of the registration.
//
// Registering with a workset group is a prerequisite to opening any of its
// worksets.
//
// If no workset group with this name and function profile exists in the
// specified call (or locally, if OM_NO_CALL specified), a new, empty
// workset group is created and assigned <wsGroupName> as its name. This
// name must be a valid workset group name.
//
// If the workset group already exists in the Call, its contents are copied
// from another node. This data transfer is made at low priority (note that
// subsequent receipt of the OM_WSGROUP_REGISTER_CON event does not indicate
// that this data transfer has completed).
//
// If there are worksets existing in the workset group, the Client will
// receive one or more OM_WORKSET_NEW_IND events after receiving the
// OM_WSGROUP_REGISTER_CON event.
//
// Note also that the contents of the workset group may be copied to this
// node in any order. Therefore, if objects in a workset reference other
// objects, the Client should not assume that the referenced object is
// present locally once the reference arrives.
//
// Clients registered for primary access to a workset group have full read
// and write access to the workset group and have a responsilibity to
// confirm destructive operations (such as workset clear and object delete,
// update and replace operations), as described in the relevant sections
// below.
//
// At most one Client per node may be registered with a given workset group
// for primary access. If a second Client attempts to register for primary
// access, OM_RC_TOO_MANY_CLIENTS is returned asynchronously via the
// OM_WSGROUP_REGISTER_CON event.
//
// On successful completion of the function, the return parameter
// <pCorrelator> points to a value which may be used by the Client to
// correlate this call with the event it generates. Notification of a
// successful registration will contain a workset group handle which the
// Client must uses subsequently when invoking other ObMan functions
// relating to this workset group.
//
// If the maximum number of workset groups in concurrent use per call has
// been reached, the OM_RC_TOO_MANY_WSGROUPS error is returned
// asynchronously. If the maximum number of workset groups in use by one
// Client is reached, OM_RC_NO_MORE HANDLES is returned synchronously. If
// ObMan cannot create a new workset group for any other reason, the
// OM_RC_CANNOT_CREATE_WSG error is returned (synchronously or
// asynchronously).
//
// Note that separate DC-Groupware tasks must each register independently
// with the workset groups they wish to use, as workset group handles may
// not be passed between tasks.
//
// Ensuing Events:
//
// Invoking this function will cause the OM_WSGROUP_REGISTER_CON event to be
// posted to the invoking Client.
//
// If ObMan is forced at some later stage to move the workset group out of
// the call for which it was intended (usually at call end time), the Client
// will receive an OM_WSGROUP_MOVE_IND event.
//
// When a Client has successfully registered with a workset group, it will
// receive OM_PERSON_JOINED_IND, OM_PERSON_LEFT_IND and
// OM_PERSON_DATA_CHANGED_IND events as primaries (including the calling
// Client) register and deregister from the workset group and change their
// person data.
//
// Return Codes:
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_NO_MORE_HANDLES
//
//
UINT OM_WSGroupRegisterS(POM_CLIENT pomClient,
UINT call,
OMFP fpHandler,
OMWSG wsg,
OM_WSGROUP_HANDLE * phWSGroup);
//
//
// Description:
//
// This is a synchronous function which requests that ObMan register a
// Client with a particular workset group for SECONDARY access. The workset
// group is determined by the following:
//
// - <call> is the DC-Groupware call which contains the workset group (or
// OM_NO_CALL if the workset group is a local workset group)
//
// - <functionProfile> is the Function Profile of the workset group
//
// - <wsGroupName> is the name of the workset group.
//
// The <pomClient> parameter is the ObMan handle returned by the OM_Register
// function.
//
// A Client may only register for secondary access for a workset group when
// there is already a local Client fully registered for primary access to
// that workset group. If there is no such local primary, OM_RC_NO_PRIMARY
// is returned.
//
// If there are worksets existing in the workset group, the Client will
// receive one or more OM_WORKSET_NEW_IND events after this function
// completes.
//
// Registering for secondary access to a workset group gives a Client the
// same access privileges as a primary Client except for:
//
// - creating worksets
//
// - moving workset groups into/out of Calls
//
// - locking worksets and objects
//
// In addition, secondary Clients of a workset group will receive events
// relating to the workset group in the same way as primary Clients.
// However, the following important difference applies: secondary Clients
// will receive notification of object deletes, updates and replaces AFTER
// the associated operations have taken place (as opposed to primary
// Clients, who are informed BEFORE action is taken and must invoke the
// relevant confirmation function).
//
// To highlight this difference, these events have a primary and a secondary
// variety:
//
//
//
// Primary Secondary
//
// - OM_WORKSET_CLEAR_IND OM_WORKSET_CLEARED_IND
// - OM_OBJECT_DELETE_IND OM_OBJECT_DELETED_IND
// - OM_OBJECT_REPLACE_IND OM_OBJECT_REPLACED_IND
// - OM_OBJECT_UPDATE_IND OM_OBJECT_UPDATED_IND
//
//
//
// Several Clients per node, up to a defined limit, may be registered with
// a given workset group for secondary access. Once this limit is reached,
// OM_RC_TOO_MANY_CLIENTS is returned.
//
// On successful completion of the function, the return parameter
// <phWSGoup> points to a workset group handle which the Client must uses
// subsequently when invoking other ObMan functions relating to this
// workset group.
//
// Note that separate DC-Groupware tasks must each register independently
// with the workset groups they wish to use, as workset group handles may
// not be passed between tasks.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_NO_MORE_HANDLES
// OM_RC_NO_PRIMARY
// OM_RC_TOO_MANY_CLIENTS
//
//
UINT OM_WSGroupMoveReq(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
UINT callID,
OM_CORRELATOR * pCorrelator);
//
//
// Description:
//
// This function requests that ObMan move a local workset group previously
// created as a local workset group (i.e. created specifying the OM_NO_CALL
// for the Call ID parameter) into the DC-Groupware Call identified by
// <callID>. If the move is successful, the workset group becomes available
// at all nodes in the Call.
//
// The workset group to move is specified by <hWSGroup>, which must be a
// valid workset group handle.
//
// If the function completes successfully, the OM_WSGROUP_MOVE_CON event is
// posted to the Client when the attempt to move the workset group into the
// Call has completed. This event indicates whether the attempt was
// successful.
//
// If there is already a (different) workset group in the specified Call
// with the same name and Function Profile, this function will fail
// asynchronously.
//
// Ensuing Events:
//
// Invoking this function causes the OM_WSGROUP_MOVE_CON to be posted to the
// invoking Client. If the move is successful, the OM_WSGROUP_MOVE_IND
// event is also posted to all local Clients which are registered with the
// workset group, including the invoking Client.
//
// Return Codes:
//
// 0 (== OK)
// OM_RC_ALREADY_IN_CALL
// Utility Service return codes
//
//
void OM_WSGroupDeregister(POM_CLIENT pomClient, OM_WSGROUP_HANDLE * phWSGroup);
//
//
// Description:
//
// This function deregisters the Client from the workset group specified by
// the handle at <phWSGroup>. Any worksets which the Client had open in
// the workset group are closed (thereby releasing all locks), and the
// Client will receive no more events relating to this workset group.
//
// This call may cause the local copy of the workset group and its worksets
// to be discarded; in this sense, this function is destructive.
//
// This call sets the value at <phWSGroup> to NULL to prevent the Client
// using this handle in further calls to ObMan.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// None
//
//
UINT OM_WorksetOpenPReq(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
NET_PRIORITY priority,
BOOL fTemp,
OM_CORRELATOR * pCorrelator);
UINT OM_WorksetOpenS(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID);
//
//
// Description:
//
// These functions open a specified workset for a Client.
//
// OM_WorksetOpenPReq is an asynchronous function which will create the
// workset if it does not exist. Only primary Clients of this workset group
// may call this function.
//
// OM_WorksetOpenS is a synchronous function which will return
// OM_RC_WORKSET_DOESNT_EXIST if the workset does not exist. Only secondary
// Clients of this workset group may call this function.
//
// In the asynchronous (primary) case, when ObMan has opened the workset for
// the Client, or failed to do so, it posts an OM_WORKSET_OPEN_CON event to
// the Client indicating success or the reason for failure. This event will
// contain the correlator value returned in <pCorrelator> by this function.
//
// If this action results in the creation of a new workset, <priority> will
// specify the MCS priority at which data relating to the workset will be
// transmitted. Note that NET_TOP_PRIORITY is reserved for ObMan's private
// use and must not be specified as the <priority> parameter.
//
// If OM_OBMAN_CHOOSES_PRIORITY is specified as the <priority> parameter,
// ObMan will prioritise data transfers according to size.
//
// If the workset already exists, the Client will receive an
// OM_OBJECT_ADD_IND event for each object that is in the workset when it is
// opened.
//
// Opening a workset is a prerequisite to performing any operations on it or
// its contents. Once a Client has opened a workset it will receive events
// when changes are made to the workset or its contents.
//
// If this Client has already opened this workset,
// OM_RC_WORKSET_ALREADY_OPEN is returned. No 'use count' of opens is
// maintained, so the first OM_WorksetClose will close the workset,
// irrespective of how many times it has been opened.
//
// Ensuing Events:
//
// Invoking OM_WorksetOpenPReq causes the OM_WORKSET_OPEN_CON event to be
// posted to the invoking Client.
//
// If this action results in the creation of a new workset, an
// OM_WORKSET_NEW_IND is posted to all Clients which are registered with the
// workset group, including the invoking Client.
//
// In both the primary and secondary cases, if the workset contains any
// objects, an OM_OBJECT_ADD_IND event will be posted to the Client for each
// one.
//
// Return Codes:
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_WORKSET_DOESNT_EXIST
// OM_RC_WORKSET_ALREADY_OPEN
//
//
#define OM_OBMAN_CHOOSES_PRIORITY (NET_INVALID_PRIORITY)
void OM_WorksetClose(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID);
//
//
// Description:
//
// This function closes the workset in <hWSGroup> identified by <worksetID>.
// The Client may no longer access this workset and will receive no more
// events relating to it. ObMan will however continue to update the
// contents of the workset in the background; in this sense, this function
// is non-destructive.
//
// When a Client closes a workset, ObMan automatically releases the
// following resources:
//
// - any locks the Client has relating to this workset
//
// - any objects which the Client had been reading or had allocated
// for writing in the workset.
//
// If indication events which require a Confirm function to be invoked have
// been received by the Client but not yet confirmed, these Confirms are
// implicitly executed by ObMan AND THE CLIENT MUST NOT SUBSEQUENTLY ATTEMPT
// TO CONFIRM THEM.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// None
//
//
void OM_WorksetFlowQuery(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
UINT* pBytesOutstanding);
//
//
// Description:
//
// A Client calls this function whenever it wishes to discover the size of
// the backlog of data relating to the workset identified by <hWSGroup> and
// <worksetID>.
//
// The "backlog" is defined as the total number of bytes of object data
// which ObMan has been given by its local Clients and which have not yet
// been acknowledged by all remote nodes where there are Clients registered
// with the workset group.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// None
//
//
UINT OM_WorksetLockReq(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
OM_CORRELATOR * pCorrelator);
//
//
// Description:
//
// This is an asynchronous function which requests a lock for the workset in
// <hWSGroup> identified by <worksetID>. When ObMan has processed the lock
// request, it will send an OM_WORKSET_LOCK_CON event to the Client
// indicating success or the reason for failure.
//
// Holding a workset lock prevents other Clients from making any changes to
// the workset or any of its objects. Specifically, the following functions
// are prohibited:
//
// - locking the same workset
//
// - locking an object in the workset
//
// - moving an object within the workset
//
// - adding an object to the workset
//
// - deleting an object from the workset
//
// - updating or replacing an object in the workset.
//
// Locking a workset does not prevent other Clients from reading its
// contents.
//
// The function will cause an assertion failure if the Client requesting the
// lock already holds or has requested a lock which is equal to or after
// this one in the Universal Locking Order.
//
// On successful completion of the function, the value at <pCorrelator> is a
// value which the Client can use to correlate the subsequent
// OM_OBJECT_LOCK_CON event with this request.
//
// A Client must release the lock when it no longer needs it, using the
// OM_WorksetUnlock function. Locks will be automatically released when a
// Client closes the workset or deregisters from the workset group.
//
// Only primary Clients of a workset group may call this function.
//
// Ensuing Events:
//
// Invoking this function will cause the OM_WORKSET_LOCK_CON event to be
// posted to the invoking Client at some later time.
//
// Return Codes:
//
// 0 (== OK)
// Utility Service return codes
//
//
void OM_WorksetUnlock(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID);
//
//
// Description:
//
// This function unlocks the workset in <hWSGroup> identified by
// <worksetID>. This must be the lock most recently acquired or requested
// by the Client; otherwise, the lock violation error causes an assertion
// failure.
//
// If this function is called before the OM_WORKSET_LOCK_CON event is
// received, the Client will not subsequently receive the event.
//
// Ensuing Events:
//
// This function causes an OM_WORKSET_UNLOCK_IND to be posted to all Clients
// which have the workset open, including the invoking Client.
//
// Return Codes:
//
// None
//
//
void OM_WorksetCountObjects(
POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
UINT* pCount);
//
//
// Description:
//
// On successful completion of this function , the value at <pCount> is the
// number of objects in the workset in <hWSGroup> identified by
// <worksetID>.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// None
//
//
UINT OM_WorksetClear(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID);
//
//
// Description:
//
// This function requests that ObMan clear (i.e. delete the contents of)
// the workset in <hWSGroup> identified by <worksetID>.
//
// When this function is invoked, all Clients with the workset open
// (including the invoking Client) are notified of the impending clear via
// the OM_WORKSET_CLEAR_IND event. In response, each Client must invoke the
// OM_WorksetClearConfirm function; its view of the workset will not be
// cleared until it has done so.
//
// Ensuing Events:
//
// This function will result in the OM_WORKSET_CLEAR_IND being posted to all
// Clients which have the workset open, including the invoking Client.
//
// Return Codes
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_WORKSET_EXHAUSTED
//
//
void OM_WorksetClearConfirm(
POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID);
//
//
// Description:
//
// A Client must call this function after it receives an
// OM_WORKSET_CLEAR_IND. When the function is invoked, ObMan clears this
// Client's view of the workset. It is bad Groupware programming practice
// for a Client to unduly delay invoking this function.
//
// Note however that this function has a purely local effect: delaying (or
// executing) a clear confirm at one node will not affect the contents of
// the workset group at any other node.
//
// It is illegal to call this function when a Client has not received an
// OM_WORKSET_CLEAR_IND event.
//
// The arguments to the function must be the same as the workset group
// handle and workset ID included with the OM_WORKSET_CLEAR_IND event.
//
// The function will fail if ObMan is not expecting a clear-confirmation for
// this workset.
//
// This function causes all objects being read in this workset, and all
// object locks in this workset, to be released (i.e. the function performs
// implicit OM_ObjectUnlock and OM_ObjectRelease functions). It does not
// cause objects allocated using OM_ObjectAlloc to be discarded.
//
// If there are indication events for object Deletes, Replaces or Confirms
// which have been posted to the Client but not yet confirmed, these
// confirmations are implicitly executed.
//
// Ensuing Events:
//
// When the primary Client of a workset group confirms a clear using this
// function, an OM_WORKSET_CLEARED_IND is posted to all local secondary
// Clients of the workset group.
//
// Return Codes:
//
// None
//
//
//
//
// Description:
//
// These functions add an object to the workset in <hWSGroup> identified by
// <worksetID>. The <ppObject> parameter is a pointer to a pointer to the
// object.
//
// The position to add the object is determined as follows:
//
// - OM_ObjectAddLast: after the last object in the workset
//
// - OM_ObjectAddFirst: before the first object in the workset
//
// - OM_ObjectAddAfter: after the object specified by <hExistingObject>
//
// - OM_ObjectAddBefore: before the object specified by <hExistingObject>.
//
// Note that the OM_ObjectAddAfter and OM_ObjectAddBefore functions require
// that the invoking Client holds a workset lock, whereas the
// OM_ObjectAddFirst and OM_ObjectAddLast functions will fail if the workset
// is locked by another Client.
//
// ------------------------------------------------------------------------
//
// Note: OM_ObjectAddAfter and OM_ObjectAddBefore are not implemented in
// DC-Groupware R1.1.
//
// ------------------------------------------------------------------------
//
// On successful completion of the function, <phNewObject> points to the
// newly created handle of the object added. The Client should use this
// handle in all subsequent ObMan calls relating to this object.
//
// The <ppObject> parameter must be a pointer to a valid object pointer
// returned by the OM_ObjectAlloc function. If the function completes
// successfully, ObMan assumes ownership of the object and the value at
// <ppObject> is set to NULL to prevent the Client using the object pointer
// again.
//
// The <updateSize> parameter is the size (in bytes) of that portion of the
// object which may be updated using the OM_ObjectUpdate function (not
// counting the <length> field).
//
// Additions to a workset will be sequenced identically at all nodes which
// have the workset open, but the actual sequence arising from simultaneous
// additions by multiple Clients cannot be predicted in advance.
//
// If a set of Clients wishes to impose a particular sequence, they can
// enforce this using an agreed locking protocol based on the workset
// locking (in most cases, it is only necessary that the order is the same
// everywhere, which is why locking is not enforced by ObMan).
//
// Ensuing Events:
//
// This function causes an OM_OBJECT_ADD_IND to be posted to all Clients
// which have the workset open, including the invoking Client.
//
// Return Codes:
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_WORKSET_LOCKED (AddFirst, AddLast only)
// OM_RC_WORKSET_EXHAUSTED
//
//
//
//
// Description:
//
// These functions move an object within a workset. The workset is
// specified by <worksetID> and <hWSGroup> and the handle of the object to
// be moved is specified by <pObj>
//
// The position to which the object is moved is determined as follows:
//
// - OM_ObjectMoveLast: after the last object in the workset
//
// - OM_ObjectMoveFirst: before the first object in the workset
//
// - OM_ObjectMoveAfter: after the object specified by <pObj2>
//
// - OM_ObjectMoveBefore: before the object specified by <pObj2>.
//
// Note that OM_ObjectMoveAfter and OM_ObjectMoveBefore require that the
// invoking Client holds a workset lock, whereas the OM_ObjectMoveFirst and
// OM_ObjectMoveLast functions will fail if the workset is locked by another
// Client.
//
// ------------------------------------------------------------------------
//
// Note: OM_ObjectMoveAfter and OM_ObjectMoveBefore are not implemented in
// DC-Groupware R1.1.
//
// ------------------------------------------------------------------------
//
// Locked objects may be moved.
//
// Neither the handle nor the ID of an object is altered by moving it within
// a workset.
//
// Ensuing Events
//
// This action causes the OM_OBJECT_MOVE_IND to be posted to all Clients
// which have the workset open, including the invoking Client.
//
// Return Codes
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_WORKSET_EXHAUSTED
// OM_RC_WORKSET_LOCKED (MoveFirst, MoveLast only)
//
//
UINT OM_ObjectDelete(
POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj);
//
//
// Description:
//
// This function requests that ObMan delete an object from a workset. The
// workset is specified by <worksetID> and <hWSGroup> and the handle of the
// object to be deleted is <pObj>.
//
// The local copy of the object is not actually deleted until the Client
// invokes OM_ObjectDeleteConfirm in response to the OM_OBJECT_DELETE_IND
// event which this function generates.
//
// When this function is invoked, all Clients with the workset open
// (including the invoking Client) are notified of the impending deletion
// via the OM_OBJECT_DELETE_IND event. In response, each Client must invoke
// the OM_ObjectDeleteConfirm function; each Client will have access to the
// object until it has done so.
//
// If this object is already pending deletion (i.e. a DELETE_IND event has
// been posted to the Client but not yet Confirmed) this function returns
// the OM_RC_OBJECT_DELETED error.
//
// ObMan guarantees not to reuse a discarded object handle in the same
// workset within the lifetime of the workset group.
//
// Ensuing Events:
//
// This function causes the OM_OBJECT_DELETE_IND to be posted to all Clients
// which have the workset open, including the invoking Client (except as
// where stated above).
//
// Return Codes:
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_WORKSET_LOCKED
// OM_RC_WORKSET_EXHAUSTED
// OM_RC_OBJECT_DELETED
//
//
void OM_ObjectDeleteConfirm(
POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj);
//
//
// Description:
//
// A Client must call this function after it receives an
// OM_OBJECT_DELETE_IND. When the function is invoked, ObMan deletes the
// object specified by <hWSGroup>, <worksetID> and the value at <ppObj>.
// It is bad Groupware programming practice for a Client to unduly delay
// invoking this function.
//
// Note however that this function has a purely local effect: delaying (or
// executing) a delete confirm at one node will not affect the contents of
// the workset group at any other node.
//
// On successful completion, the handle of the deleted object becomes
// invalid and the value at <ppObj> is set to NULL to prevent the Client
// from further accessing this object.
//
// Any pointer to the previous version of this object which the Client had
// obtained using OM_ObjectRead becomes invalid and should not be referred
// to again (i.e. the function performs an implicit OM_ObjectRelease).
//
// The function will cause an assertion failure if ObMan is not expecting a
// delete-confirmation for this object.
//
// Ensuing Events:
//
// When the primary Client of a workset group confirms a delete using this
// function, an OM_OBJECT_DELETED_IND is posted to all local secondary
// Clients of the workset group.
//
// Return Codes:
//
// None
//
//
UINT OM_ObjectReplace(
POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj,
POM_OBJECTDATA * ppData);
UINT OM_ObjectUpdate(
POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj,
POM_OBJECTDATA * ppData);
//
//
// Description:
//
// This function requests that ObMan replaces/updates the object specified
// by <pObj> in the workset specified by <worksetID> and <hWSGroup>.
//
// "Replacing" one object with another causes the previous object to be
// lost. "Updating" an object causes only the first N bytes of the object
// to be replaced by the <data> field of the object supplied, where N is the
// <length> field of the update. The rest of the object data remains the
// same, as does the length of the object.
//
// The local copy of the object is not actually replaced/updated until the
// Client invokes OM_ObjectReplaceConfirm/OM_ObjectUpdateConfirm in response
// to the OM_OBJECT_REPLACE_IND/OM_OBJECT_UPDATE_IND which this function
// generates.
//
// The <ppObject> parameter must be a pointer to a valid object pointer
// returned by the OM_ObjectAlloc function. If the function completes
// successfully, ObMan assumes ownership of the object and the value at
// <ppObject> is set to NULL to prevent the Client using the object pointer
// again.
//
// Neither the handle nor the ID of an object is altered by replacing or
// updating the object.
//
// If the object is pending deletion i.e. if ObMan has posted an
// OM_OBJECT_DELETE_IND event which has not yet been confirmed, the
// OM_RC_OBJECT_DELETED error is returned.
//
// If the object is pending replace or update i.e. if ObMan has posted an
// OM_OBJECT_REPLACE_IND/OM_OBJECT_UPDATE_IND event which has not yet been
// confirmed, this replace/update spoils the previous one. In this case, no
// further event is posted, and when the outstanding event is confirmed, the
// most recent replace/update is performed.
//
// The <reserved> parameter to OM_ObjectUpdate is not used in DC-Groupware
// R1.1 and must be set to zero.
//
// For a replace, the size of the object specified by <ppObject> must be at
// least as large as the <updateSize> specified when the object was
// originally added.
//
// For an update, the size of the object specified by <ppObject> must be the
// same as the <updateSize> specified when the object was originally added.
//
// Object replaces/updates will be sequenced identically at all nodes, but
// the actual sequence arising from simultaneous replace/update operations
// by multiple Clients cannot be predicted in advance.
//
// If a set of Clients wishes to impose a particular sequence, they should
// use an agreed locking protocol based on object or workset locking (in
// most cases, it is only necessary that the order is the same everywhere,
// which is why locking is not enforced by ObMan).
//
// Replaces and updates may be spoiled by ObMan so Client should not assume
// that an event will be generated for each replace or update carried out.
//
// Ensuing Events:
//
// This function causes the OM_OBJECT_REPLACE_IND/OM_OBJECT_UPDATE_IND to be
// posted to all Clients which have the workset open, including the invoking
// Client.
//
// Return Codes:
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_WORKSET_LOCKED
// OM_RC_OBJECT_LOCKED
// OM_RC_OBJECT_DELETED
//
//
void OM_ObjectReplaceConfirm(
POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj);
void OM_ObjectUpdateConfirm(
POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj);
//
//
// Description:
//
// When a Client receives an OM_OBJECT_REPLACE_IND/OM_OBJECT_UPDATE_IND, it
// must confirm the relevant operation by calling OM_ObjectReplaceConfirm or
// OM_ObjectUpdateConfirm.
//
// When the functions are invoked, ObMan replaces/updates the object
// specified by <hWSGroup>, <worksetID> and <pObj>. It is bad Groupware
// programming practice for a Client to unduly delay invoking this function.
//
// Note however that these functions have a purely local effect: delaying
// (or executing) replace/update confirms at one node will not affect the
// contents of the workset group at any other node.
//
// Any pointer to the previous version of this object which the Client had
// obtained using OM_ObjectRead becomes invalid and should not be referred
// to again (i.e. the functions perform an implicit OM_ObjectRelease).
//
// The functions will cause an assertion failure if ObMan is not expecting a
// replace- or update-confirmation for this object.
//
// Ensuing Events:
//
// When the primary Client of a workset group confirms an update or replace
// using this function, an OM_OBJECT_UPDATED_IND/OM_OBJECT_REPLACED_IND is
// posted to all local secondary Clients of the workset group.
//
// Return Codes
//
// None
//
//
UINT OM_ObjectLockReq(POM_CLIENT pomClient, OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID, POM_OBJECT pObj, BOOL reserved,
OM_CORRELATOR * pCorrelator);
//
//
// Description:
//
// This is an asynchronous function which requests a lock for the object
// specified by <pObj> in the workset identified by <worksetID> and
// <hWSGroup>. When ObMan has processed the lock request, it will send an
// OM_OBJECT_LOCK_CON to the Client indicating success or the reason for
// failure.
//
// ------------------------------------------------------------------------
//
// Note: OM_ObjectLockReq and OM_ObjectUnlock are not implemented in
// DC-Groupware R1.1.
//
// ------------------------------------------------------------------------
//
// Holding an object lock prevents other Clients from
//
// - locking the workset
//
// - locking the object
//
// - updating or replacing the object
//
// - deleting the object.
//
// It does not prevent other Clients from reading the object or moving it
// within a workset.
//
// The function will cause an assertion failure if the Client requesting the
// lock already holds or has requested a lock which is equal to or after
// this one in the Universal Locking Order.
//
// On successful completion of the function, the value at <pCorrelator> is a
// value which the Client can use to correlate the subsequent
// OM_OBJECT_LOCK_CON event with this request.
//
// The <reserved> parameter is not used in DC-Groupware R1.1 and must be set
// to zero.
//
// A Client must release the lock when it no longer needs it, using the
// OM_ObjectUnlock function. Locks will be automatically released when a
// Client closes the workset or deregisters from the workset group.
//
// Ensuing Events:
//
// Invoking this function will cause the OM_OBJECT_LOCK_CON event to be
// posted to the invoking Client at some later time.
//
// Return Codes:
//
// 0 (== OK)
// Utility Service return codes
//
//
void OM_ObjectUnlock(
POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj);
//
//
// Description:
//
// This function unlocks the object specified by <worksetID> and <pObj>.
// This must be the lock most recently acquired or requested by the Client;
// otherwise, the lock violation error causes an assertion failure.
//
// If this function is called before the OM_OBJECT_LOCK_CON event is
// received, the Client will not subsequently receive the event.
//
// ------------------------------------------------------------------------
//
// Note: OM_ObjectLockReq and OM_ObjectUnlock are not implemented in
// DC-Groupware R1.1.
//
// ------------------------------------------------------------------------
//
// Ensuing Events:
//
// This function causes an OM_OBJECT_UNLOCK_IND to be posted to all other
// Clients which have the workset open.
//
// Return Codes:
//
// None
//
//
UINT OM_ObjectH(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObjOther,
POM_OBJECT * pObj,
OM_POSITION omPos);
UINT OM_ObjectRead(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj,
POM_OBJECTDATA * ppData);
//
//
// Description:
//
// This function enables a Client to read the contents of an object by
// converting an object handle into a pointer to the object.
//
// On successful completion, the value at <ppObject> points to the specified
// object.
//
// Invoking this function causes the object to be held in memory at the
// location indicated by the return value at <ppObject>. When it has
// finished reading the object, the Client must release it using the
// OM_ObjectRelease function. Holding object pointer for extended lengths
// of time may adversely affect ObMan's ability to efficiently manage its
// memory.
//
// This pointer is valid until the Client releases the object, either
// explicitly with OM_ObjectRelease or implicitly.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// 0 (== OK)
// Utility Service return codes
//
//
void OM_ObjectRelease(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj,
POM_OBJECTDATA * ppData);
//
//
// Description:
//
// Calling this function indicates to ObMan that the Client has finished
// reading the object specified by the handle <pObj>. The <ppObject>
// parameter is a pointer to a pointer to the object, which was previously
// obtained using OM_ObjectRead.
//
// On successful completion, the pointer to this object which the Client
// acquired using OM_ObjectRead becomes invalid (as the object may
// subsequently move in memory) and the value at <ppObject> is set to NULL
// to prevent the Client from using it again.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// None
//
//
UINT OM_ObjectAlloc(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
UINT length,
POM_OBJECTDATA * ppData);
//
//
// Description:
//
// This function allocates a new, empty object the <data> field of which is
// <length> bytes long. The object must be intended for subsequent
// insertion into the workset specified by <hWSGroup> and <worksetID>.
//
// Note that the <length> parameter is the length of the object's data field
// (so the total memory requirement for this function is length+4 bytes).
//
// The contents of the memory allocated are undefined, and it is the
// Client's responsibility to fill in the <length> field at the start of the
// object.
//
// On successful completion, the value at <ppObject> points to the new
// object. This pointer is valid until the Client returns the object to
// ObMan using one of the functions mentioned here.
//
// A Client may write in this object and will normally insert it in the
// workset for which it was allocated using one of the object add, update or
// replace functions. However, if a Client fails to do so or decides for
// some other reason not to do so, it must free up the object by calling the
// OM_ObjectDiscard function.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// 0 (== OK)
// Utility Service return codes
//
//
void OM_ObjectDiscard(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECTDATA * ppData);
//
//
// Description:
//
// This function discards an object which a Client previously allocated
// using OM_ObjectAlloc. A Client will call this function if for some
// reason it does not want to or cannot insert the object into the workset
// for which it was allocated. A Client must not call this function for an
// object which it has already added to a workset or used to update or
// replace an object in a workset.
//
// On successful completion, the value at <ppObject> is set to NULL to
// prevent the Client from accessing the object again.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// None
//
//
UINT OM_ObjectIDToPtr(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
OM_OBJECT_ID objectID,
POM_OBJECT * ppObj);
//
//
// Description:
//
// This functions converts an object ID to an object handle. If no object
// with the specified ID is found in the specified workset, an error is
// returned.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// 0 (== OK)
// OM_RC_BAD_OBJECT_ID
//
//
void OM_ObjectPtrToID(POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
OM_WORKSET_ID worksetID,
POM_OBJECT pObj,
POM_OBJECT_ID pObjectID);
//
//
// Description:
//
// This functions converts an object handle to an object ID.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// None
//
//
//
//
// Description
//
// These functions return information about a particular primary Client
// (identified by <hPerson>) of the workset group identified by <hWSGroup>
// <function profile> combination.
//
// If the person handle <hPerson> is invalid, the OM_RC_NO_SUCH_PERSON error
// is returned.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// 0 (== OK)
// OM_RC_NO_SUCH_PERSON
// Utility Service return codes
//
//
UINT OM_GetNetworkUserID(
POM_CLIENT pomClient,
OM_WSGROUP_HANDLE hWSGroup,
NET_UID * pNetUserID);
//
//
// Description:
//
// This functions returns ObMan's Network user ID for the call which
// contains the workset group specified by <hWSGroup>.
//
// This network ID corresponds to the <creator> field of objects defined by
// the Object Manager Function Profile.
//
// If the specified workset group is a local workset group (i.e. its
// "call" is OM_NO_CALL), then the function returns OM_RC_LOCAL_WSGROUP.
//
// Ensuing Events:
//
// None
//
// Return Codes:
//
// 0 (== OK)
// OM_RC_LOCAL_WSGROUP
//
//
BOOL CALLBACK OMSEventHandler(LPVOID pomClient, UINT event, UINT_PTR param1, UINT_PTR param2);
//
//
// Description
//
// This is the handler that ObMan registers (as a Utility Service event
// handler) for Client tasks to trap ObMan events. It serves two main
// purposes:
//
// - some state changes associated with events posted to Client tasks
// are better made when the event arrives than when it is posted
//
// - this handler can detect and discard "out-of-date" events, such as
// those arriving for a workset which a Client has just closed.
//
// The first parameter is the Client's ObMan handle, as returned by
// OM_Register, cast to a UINT.
//
// The second parameter is the event to be processed.
//
// The third and fourth parameters to the function are the two parameters
// associated with the event.
//
//
//
//
// OM_OUT_OF_RESOURCES_IND
//
// Description:
//
// This abnormal failure event is posted when ObMan cannot allocate
// sufficient resources to complete a particular action, usually one
// prompted by a network event.
//
// Clients should treat this event as a fatal error and attempt to
// terminate.
//
// The parameters included with the event are reserved.
//
//
//
//
// OM_WSGROUP_REGISTER_CON
//
// Description:
//
// This event is posted when ObMan has finished processing a request to
// register a Client with a workset group. The parameters included with
// the event are defined as follows:
//
// - the second parameter is an OM_EVENT_DATA32 structure:
//
// - the <correlator> field is the value which was returned by
// the corresponding invocation of the OM_WSGroupRegisterPReq
// function
//
// - the <result> field is one of
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_OUT_OF_RESOURCES
// OM_RC_TOO_MANY_CLIENTS
// OM_RC_TOO_MANY_WSGROUPS
// OM_RC_ALREADY_REGISTERED
// OM_RC_CANNOT_CREATE_WSG
//
// - if the <result> field is 0 (== OK), the first parameter is an
// OM_EVENT_DATA16 structure which contains a newly created handle
// to the workset group involved (the <worksetID> field is
// reserved).
//
// Once a Client has received this notification, it will receive
// OM_WORKSET_NEW_IND events to notify it of the existing worksets in the
// group, if there are any.
//
//
//
//
// OM_WSGROUP_MOVE_CON
//
// Description:
//
// This event is posted when ObMan has finished processing a request to
// move an existing workset group into a Call. The parameters included
// with the event are defined as follows:
//
// - the second parameter is an OM_EVENT_DATA32 structure:
//
// - the <correlator> field is the value which was returned by
// the corresponding invocation of the OM_WSGroupMoveReq
// function
//
// - the <result> field is one of
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_CANNOT_MOVE_WSGROUP
//
// - the first parameter is a OM_EVENT_DATA16 structure which contains the
// handle of the workset group involved (the <worksetID> field is
// reserved).
//
//
//
//
// OM_WSGROUP_MOVE_IND
//
// Description:
//
// This event is posted when ObMan has moved a workset group either into or
// out of a Call.
//
// This will happen
//
// - when the workset group is moved out of a Call (because e.g. the call
// has ended), thus becoming a local workset group
//
// - when a local Client requests to move a local workset group into a
// Call.
//
// The parameters included with the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset group involved (the <worksetID> field is reserved).
//
// - the second parameter is the handle of the Call into which the
// workset group has been moved (== OM_NO_CALL when the workset group
// has been moved out of a Call).
//
// If the workset group has been moved out of a call, it continues in
// existence as a local workset group and the Client may continue to use it
// as before. However, no updates will be sent to or received from Clients
// residing on other nodes.
//
// If a Client wishes to move this workset group into another Call, it can
// do so using the OM_WSGroupMoveReq function. Note that an attempt to move
// the workset group back into the same Call is likely to fail due to a name
// clash since the original version probably still exists in the Call.
//
// This event may also be prompted by the failure to allocate memory for a
// large object being transferred from another node.
//
//
//
//
// OM_WORKSET_NEW_IND
//
// Description:
//
// This event is posted when a new workset has been created (by the
// receiving Client or by another Client). The parameters included with
// the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset involved
//
// - the second parameter is reserved.
//
//
//
//
// OM_WORKSET_OPEN_CON
//
// Description:
//
// This event is posted when ObMan has finished processing a request to
// open a workset for a specific Client.
//
// The parameters included with the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset involved
//
// - the second parameter is a OM_EVENT_DATA32 structure:
//
// - the <correlator> field is the correlator which was
// returned by the corresponding invocation of the
// OM_WorksetOpenReq function
//
// - the <result> field is one of
//
// 0 (== OK)
// OM_RC_OUT_OF_RESOURCES.
//
// In all but the case of OK, the open request has failed and the Client
// does not have the workset open.
//
//
//
//
// OM_WORKSET_LOCK_CON
//
// Description:
//
// This event is posted to a Client when ObMan has succeeded in obtaining,
// or failed to obtain, a workset lock which the Client had requested. The
// parameters included with the event are as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset involved
//
// - the second parameter is a OM_EVENT_DATA32 structure:
//
// - the <correlator> field is the correlator which was
// returned by the corresponding invocation of the
// OM_WorksetLockReq function
//
// - the <result> field is one of
//
// 0 (== OK)
// OM_RC_OUT_OF_RESOURCES
// OM_RC_WORKSET_LOCKED
// OM_RC_OBJECT_LOCKED.
//
// In all but the case of OK, the lock request has failed and the Client
// does not hold the lock.
//
//
//
//
// OM_WORKSET_UNLOCK_IND
//
// Description:
//
// This event is posted when a workset is unlocked using the
// OM_WorksetUnlock function. The parameters included with the event are
// as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset involved
//
// - the second parameter is reserved.
//
//
//
//
// OM_WORKSET_CLEAR_IND
//
// Description:
//
// This event is posted (to primary Clients only) after a local or remote
// Client has invoked the OM_WorksetClear function. After a Client receives
// this event, it must call OM_WorksetClearConfirm to enable ObMan to clear
// the workset.
//
// The parameters included with the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset involved
//
// - the second parameter is reserved.
//
//
//
//
// OM_WORKSET_CLEARED_IND
//
// Description:
//
// This event is posted (to secondary Clients only) when a workset has been cleared.
//
// The parameters included with the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset involved
//
// - the second parameter is reserved.
//
//
//
//
// OM_OBJECT_ADD_IND
//
// Description:
//
// This event is posted after a new object has been added to a workset.
//
// The parameters to the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset group and workset involved
//
// - the second parameter is the handle of the object involved.
//
//
//
//
// OM_OBJECT_MOVE_IND
//
// Description:
//
// This event is posted after a new object has been moved within a workset.
//
// The parameters to the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset group and workset involved
//
// - the second parameter is the handle of the object involved.
//
//
//
//
// OM_OBJECT_DELETE_IND
//
// Description:
//
// This event is posted (to primary Clients only) after a local or remote
// Client has invoked the OM_ObjectDelete function. After a Client
// receives this event, it must call OM_ObjectDeleteConfirm to enable ObMan
// to delete the object.
//
// The parameters to the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset group and workset involved
//
// - the second parameter is the handle of the object involved.
//
// See also OM_OBJECT_DELETED_IND.
//
//
//
//
// OM_OBJECT_REPLACE_IND
// OM_OBJECT_UPDATE_IND
//
// Description:
//
// These events are posted (to primary Clients only) after a local or remote
// Client has invoked the OM_ObjectReplace/OM_ObjectUpdate function. After
// a Client receives this event, it must call OM_ObjectReplaceConfirm/
// OM_ObjectUpdateConfirm to enable ObMan to replace/update the object.
//
// The parameters to the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset group and workset involved
//
// - the second parameter is the handle of the object involved.
//
// See also OM_OBJECT_REPLACED_IND/OM_OBJECT_UPDATED_IND.
//
//
//
//
// OM_OBJECT_DELETED_IND
//
// Description:
//
// This event is posted (to secondary Clients only) when an object has been
// deleted from a workset. The handle it contains is thus invalid and can
// only be used to cross-reference against lists maintained by the Client.
//
// The Client must not invoke the OM_ObjectDeleteConfirm function on
// receipt of this event.
//
// The parameters to the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 identifying the workset
// group and workset involved
//
// - the second parameter is the handle of the object involved.
//
//
//
//
// OM_OBJECT_REPLACED_IND
// OM_OBJECT_UPDATED_IND
//
// Description:
//
// These events are posted (to secondary Clients only) when an object has
// been replaced or updated. When the Client receives this event, the
// previous data is thus inaccessible.
//
// The Client must not invoke the OM_ObjectReplaceConfirm/
// OM_ObjectUpdateConfirm function on receipt of this event.
//
// The parameters to the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 identifying the workset
// group and workset involved
//
// - the second parameter is the handle of the object involved.
//
//
//
//
// OM_OBJECT_LOCK_CON
//
// Description:
//
// This event is posted to a Client when ObMan has succeeded in obtaining
// (or failed to obtain) an object lock which the Client had requested.
// The parameters included with the event are defined as follows:
//
// - the first parameter is reserved
//
// - the second parameter is a OM_EVENT_DATA32 structure:
//
// - the <correlator> field is the correlator which was
// returned by the corresponding invocation of the
// OM_ObjectLockReq function
//
// - the <result> field is one of
//
// 0 (== OK)
// Utility Service return codes
// OM_RC_WORKSET_LOCKED
// OM_RC_OBJECT_LOCKED.
//
// In all but the case of OK, the lock request has failed and the Client
// does not hold the lock.
//
//
//
//
// OM_OBJECT_UNLOCK_IND
//
// Description:
//
// This event is posted when a Client has released an object lock using the
// OM_ObjectUnlock function.
//
// The parameters to the event are defined as follows:
//
// - the first parameter is a OM_EVENT_DATA16 which identifies the
// workset group and workset involved
//
// - the second parameter is the handle of the object involved.
//
//
//
//
// OM_PERSON_JOINED_IND
// OM_PERSON_LEFT_IND
// OM_PERSON_DATA_CHANGED_IND
//
// Description:
//
// These events inform clients registered with a workset group when clients
// register with the workset group, deregister from it and set their person
// data, respectively.
//
// A client will also receive the appropriate events when it performs these
// actions itself.
//
// Parameters:
//
// The first parameter in an OM_EVENT_DATA16 which identifies the workset
// group to which the event relates. The <worksetID> field of the structure
// is undefined.
//
// The second parameter is the POM_EXTOBJEECT for the person to which the
// event relates. These handles are not guaranteed to be still valid. In
// particular, the handle received on the OM_PERSON_LEFT_IND is never valid.
// If a client wishes to correlate these events with a list of clients,
// then it is responsible for maintaining the list itself.
//
//
//
// OMP_Init()
// OMP_Term()
//
BOOL OMP_Init(BOOL * pfCleanup);
void OMP_Term(void);
void CALLBACK OMPExitProc(LPVOID pomPrimary);
BOOL CALLBACK OMPEventsHandler(LPVOID pomPrimary, UINT event, UINT_PTR param1, UINT_PTR param2);
//
//
// ProcessNetData(...)
//
// This function is called when a NET_EV_SEND_INDICATION event is received,
// indicating the arrival of a message from another instance of ObMan. The
// function determines which OMNET_... message is contained in the network
// packet and invokes the appropriate ObMan function to process the
// message.
//
//
void ProcessNetData(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
PNET_SEND_IND_EVENT pNetEvent);
//
//
// ProcessNetDetachUser(...)
//
// This function is called when a NET_EV_DETACH_INDICATION event is received
// from the network layer.
//
// The function determines whether
//
// - we have been thrown out of the Domain by MCS, or
//
// - someone else has left/been thrown out
//
// and calls ProcessOwnDetach or ProcessOtherDetach as appropriate.
//
//
void ProcessNetDetachUser(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain,
NET_UID userID);
//
//
// ProcessNetAttachUser(...)
//
// This function is called when a NET_ATTACH_INDICATION event is received
// from the network layer. The function calls MG_ChannelJoin to join us
// to our own single-user channel.
//
//
void ProcessNetAttachUser(POM_PRIMARY pomPrimary, POM_DOMAIN pDomain,
NET_UID userID, NET_RESULT result);
//
//
// ProcessNetJoinChannel(...)
//
// This function is called when a NET_EV_JOIN_CONFIRM event is received from
// the network layer. This function determines whether the join was
// successful and whether the channel joined is
//
// - our own single-user channel
//
// - the well-known ObManControl channel
//
// - a regular workset group channel
//
// and then takes appropriate action.
//
//
void ProcessNetJoinChannel(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
PNET_JOIN_CNF_EVENT pNetJoinCnf);
//
//
// ProcessNetLeaveChannel(...)
//
// This function is called when a NET_EV_LEAVE_INDICATION event is received
// from the network layer, indicating that we've been thrown out of a
// channel. This function determines whether the channel is
//
// - our own single-user channel
//
// - the well-known ObManControl channel
//
// - a regular workset group channel
//
// and then takes appropriate action; in the first two cases, this means
// behaving as if we've been thrown out of the Domain altogether, whereas
// we treat the last case just as if a Client had asked to move the workset
// group into the local Domain.
//
//
UINT ProcessNetLeaveChannel(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
NET_CHANNEL_ID channel);
//
//
// DomainRecordFindOrCreate(...)
//
//
UINT DomainRecordFindOrCreate(POM_PRIMARY pomPrimary,
UINT callID,
POM_DOMAIN * ppDomain);
//
//
// DomainDetach(...)
//
void DomainDetach(POM_PRIMARY pomPrimary, POM_DOMAIN * ppDomain, BOOL fExit);
//
//
// DeregisterLocalClient(...)
//
// This function is called by the ObMan task after the local client
// deregisters from a workset group. It causes this node's person object
// for the workset group to be deleted.
//
// If this node was the last node to be registered with the workset group,
// it also causes the relevant INFO object to be discarded.
//
// If this in turn causes the last workset group in the domain (which must
// be ObManControl) to be removed, ObMan is detached from the domain and
// the domain record becomes invalid. In this case, the ppDomain
// pointer passed in is nulled out.
//
//
void DeregisterLocalClient(POM_PRIMARY pomPrimary,
POM_DOMAIN * ppDomain,
POM_WSGROUP pWSGroup,
BOOL fExit);
//
//
// GetOMCWorksetPtr(...)
//
// This function derives a pointer to a specified workset in the
// ObManControl workset in the specified Domain.
//
//
UINT GetOMCWorksetPtr(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OM_WORKSET_ID worksetID,
POM_WORKSET * ppWorkset);
//
//
// SayWelcome(...)
//
// This function is called
//
// - when the "top" ObMan finishes initalizing (the WELCOME is broadcast)
//
// - when ObMan receives an HELLO message from a late joiner (the WELCOME
// is sent to the late joiner)
//
//
UINT SayWelcome(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
NET_CHANNEL_ID channel);
//
//
// ProcessWelcome(...)
//
// Called when a WELCOME is received from another node. This may be in
// response to a HELLO, or it may be the "top" ObMan announcing the
// completion of its initialization.
//
// If this is the first WELCOME we've got for this Domain, we merge the
// capabilities and reply to the sender, asking for a copy of the
// ObManControl workset group.
//
//
UINT ProcessWelcome(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POMNET_JOINER_PKT pWelcomePkt,
UINT lengthOfPkt);
//
//
// SayHello(...)
//
// Called when we join that domain and determine that we're not the "top"
// ObMan. We expect a WELCOME to come in response. We include our
// capabilities in the broadcast HELLO packet so that everyone knows what
// we support.
//
//
UINT SayHello(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain);
//
//
// ProcessHello(...)
//
// Called when we get a HELLO from another node. If we've completed our
// own initialization in the domain, we merge in that node's capabilities,
// then we respond with a WELCOME.
//
//
UINT ProcessHello(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POMNET_JOINER_PKT pHelloPkt,
UINT lengthOfPkt);
//
//
// MergeCaps(...)
//
// Called by ProcessHello and ProcessWelcome to merge in capabilities
// received in the packet (which will be, respectively, a late joiner's
// capabilities or the domain-wide capabilities as determined by the sender
// of the WELCOME).
//
//
void MergeCaps(POM_DOMAIN pDomain,
POMNET_JOINER_PKT pJoinerPkt,
UINT lengthOfPkt);
//
//
// ProcessOwnDetach(...)
//
// This function is called when a NET_EV_DETACH_INDICATION is received for a
// user ID that matches our own. The function moves all of the workset
// groups for this Domain into ObMan's own "local" Domain.
//
//
UINT ProcessOwnDetach(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain);
//
//
// ProcessOtherDetach(...)
//
// This function is called when a NET_EV_DETACH_INDICATION is received for a
// user ID that doesn't match our own. The function examines each workset
// in the ObManControl workset group and deletes any registration objects
// that the departed node may have put there.
//
// If any local Clients have any of these worksets open, they are informed
// of the delete. However, OBEJCT_DELETE messages are not broadcast
// throughout the Domain, since each ObMan will do them locally.
//
//
UINT ProcessOtherDetach(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
NET_UID detachedUserID);
//
//
// WSGRegisterStage1(...)
//
// This function is ObMan's handler for the OMINT_EVENT_WSGROUP_REGISTER
// function. It is the first step in the chain of functions running in the
// ObMan context which are invoked during the workset group registration
// process (the OM_WSGroupRegisterReq, running in the Client context,
// posted the original OMINT_EVENT_WSGROUP_REGISTER event).
//
// This function ensures that we are fully attached to the Domain (if not,
// it starts the Domain attach procedure and reposts a delayed
// OMINT_EVENT_WSGROUP_REGISTER event) and then starts the process of locking
// workset #0 in the ObManControl workset group.
//
//
void WSGRegisterStage1(POM_PRIMARY pomPrimary,
POM_WSGROUP_REG_CB pRegistrationCB);
//
// ProcessOMCLockConfirm(...)
//
void ProcessOMCLockConfirm(POM_PRIMARY pomPrimary, OM_CORRELATOR cor, UINT result);
//
// ProcessCheckpoint(...)
//
void ProcessCheckpoint(POM_PRIMARY pomPrimary, OM_CORRELATOR cor, UINT result);
//
//
// WSGRegisterStage2(...)
//
// This function is called when we have successfully locked workset #0 in
// ObManControl.
//
// The function checks workset #0 in ObManControl to see if the workset
// group we're trying to register the Client with already exists in the
// Domain.
//
// If it does, it finds the channel number and requests to join the
// channel.
//
// If it doesn't, it requests to join a new channel and also calls
// WSGGetNewID to generate a new workset group ID (unique within the
// Domain).
//
//
void WSGRegisterStage2(POM_PRIMARY pomPrimary,
POM_WSGROUP_REG_CB pRegistrationCB);
//
//
// WSGGetNewID(...)
//
// This function is called by WSGRegisterStage2 to generate a new workset
// group ID, in the case where the workset group doesn't already exist.
//
// It also creates a new workset in ObManControl with the same ID as the ID
// just generated. This workset which will hold the registration objects
// for the new workset group
//
//
UINT WSGGetNewID(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP_ID pWSGroupID);
//
//
// WSGRegisterStage3(...)
//
// This function is called by ProcessNetJoinChannel when a Join event
// arrives for a workset group channel.
//
// Depending on whether or not the workset was created in Stage2, the
// function calls WSGAnnounce (if it was) or WSGCatchUp (if it was not).
//
// It then unlocks the ObManControl workset and calls WSGRegisterResult.
//
//
void WSGRegisterStage3(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP_REG_CB pRegistrationCB,
NET_CHANNEL_ID channelID);
//
//
// CreateAnnounce(...)
//
// This function is called by WSGRegisterStage3 after we have joined the
// channel for a new workset group.
//
// The function announces the new workset group throughout the Domain by
// adding an object containing the name, Function Profile, ObMan ID and MCS
// channel of the workset group to workset #0 in ObManControl.
//
// Note that this "announcement" cannot be made before the Join completes
// since we only learn the ID of the channel joined when we receive the
// Join event.
//
//
UINT CreateAnnounce(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup);
//
//
// RegAnnounceBegin(...)
//
// This function adds a registration object to a workset in ObManControl.
// The workset is determined by the ID of the workset group identified by
// <pWSGroup>.
//
// This function is called
//
// - when ObMan creates a workset group
//
// - when ObMan receives a request to send a workset group to a late
// joiner.
//
// In the first case, the registration object identifies this node's use of
// the workset group. In the second case, the reg object identifies the
// late joiner's use of the workset group.
//
// The object ID returned is the ID of the reg object added.
//
//
UINT RegAnnounceBegin(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup,
NET_UID nodeID,
POM_OBJECT * ppObjReg);
//
//
// RegAnnounceComplete(...)
//
// This function is called when we are fully caught up with a workset group
// we have joined, either because we have received the SEND_COMPLETE
// message or because we just created the group ourselves.
//
// The function updates the reg object specified by <regObjectID> by
// changing the <status> field to READY_TO_SEND.
//
//
UINT RegAnnounceComplete(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup);
//
//
// WSGCatchUp(...)
//
// This function is called by Stage3 after we have joined the channel
// belonging to a workset group which already exists in the Domain.
//
// The function examines ObManControl to find the MCS ID of an instance of
// ObMan which claims to have a copy of this workset group, then sends it a
// request to transfer the workset group.
//
// The function also posts a delayed timeout event so we don't wait for
// ever to get a workset group from a particular node (this timeout is
// processed in ProcessWSGSendTimeout).
//
//
UINT WSGCatchUp(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup);
//
//
// WSGRegisterResult(...)
//
// This function is called wherever any of the workset group registration
// functions have done enough processing to know the outcome of the
// registration attempt. If all is well, it will be called by Stage3 but
// it may also be called earlier if an error occurs.
//
// The function posts an OM_WSGROUP_REGISTER_CON event to the Clientn which
// initiated the workset group registration.
//
//
void WSGRegisterResult(POM_PRIMARY pomPrimary,
POM_WSGROUP_REG_CB pRegistrationCB,
UINT result);
//
//
// WSGRegisterRetry(...)
//
// This function is called wherever any of the workset group registration
// functions encounter a recoverable "error" situation, such as failing to
// get the ObManControl lock.
//
// This function checks if we've exceeded the retry count for this
// registration attempt and if not, reposts the OMINT_EVENT_WSGROUP_REGISTER
// event, so that the whole process is started again from Stage1.
//
// If we've run out of retries, WSGRegisterResult is invoked to post
// failure to the Client.
//
//
void WSGRegisterRetry(POM_PRIMARY pomPrimary,
POM_WSGROUP_REG_CB pRegistrationCB);
//
//
// ProcessSendReq(...)
//
// This function is called when an OMNET_WSGROUP_SEND_REQ message is
// received from another node (i.e. a late joiner)
//
// The function starts the process of sending the workset group contents to
// the late joiner.
//
//
void ProcessSendReq(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POMNET_WSGROUP_SEND_PKT pSendReq);
//
//
// SendWSGToLateJoiner(...)
//
// This function is called when the checkpointing for a workset has
// completed. It sends the contents to the late joiner node.
//
//
void SendWSGToLateJoiner(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup,
NET_UID lateJoiner,
OM_CORRELATOR remoteCorrelator);
//
//
// ProcessSendMidway(...)
//
// This function is called when an OMNET_WSGROUP_SEND_MIDWAY message is
// received from another node (the node which was helping us by sending us
// the contents of a workset group).
//
// We have now received all the WORKSET_CATCHUP messages (one for each
// workset). If all is well we inform the client that registration has
// been successful and set the workset group state to
// PENDING_SEND_COMPLETE.
//
//
void ProcessSendMidway(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POMNET_WSGROUP_SEND_PKT pSendMidwayPkt);
//
//
// ProcessSendComplete(...)
//
// This function is called when an OMNET_WSGROUP_SEND_COMPLETE message is
// received from another node (the node which was helping us by sending us
// the contents of a workset group).
//
// If this message relates to the ObManControl workset group, we now have
// most of the ObManControl workset group (only "most" since there could be
// some recent objects still flying around).
//
// However, we do know that we have ALL the contents of workset #0 in
// ObManControl, since that workset is only ever altered under lock.
//
// Accordingly, we now consider ourselves to be fully-fledged members of
// the Domain, in the sense that we can correctly process our Clients
// requests to register with workset groups.
//
// If the message relates to another workset group, we now have enough of
// its contents to consider ourselves eligible to help other late joiners
// (as we have just been). Therefore, we announce this eligibilty
// throughout the Domain (using the ObManControl workset group).
//
//
UINT ProcessSendComplete(
POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POMNET_WSGROUP_SEND_PKT pSendCompletePkt);
//
//
// MaybeRetryCatchUp(...)
//
// This function is called on receipt of a DETACH indication from MCS or a
// SEND_DENY message from another node. In both cases we compare the
// helperNode field in OM_WSGROUP structure with the userID and if they
// match then we retry the catch up.
//
// Depending on the workset group status we do the following:
//
// PENDING_SEND_MIDWAY : Retry the registration from the top.
// PENDING_SEND_COMPLETE : Just repeat the catch up.
//
// If there is no one to catch up from then we do the following depending
// on the workset group status:
//
// PENDING_SEND_MIDWAY : Retry the registration from the top. Regardless
// of whether someone else is out there or not (they cannot be in the
// READY_TO_SEND state) we will end up in a consistent state.
//
// PENDING_SEND_COMPLETE : If two (or more) nodes are in this state and
// catching up from the same box who then leaves, they will have two
// partial sets of objects one of which may or may not be a subset of the
// other. If there is no one else in the READY_TO_SEND state then each
// node needs to obtain a copy of all the objects in a given workset at the
// other node.
//
//
void MaybeRetryCatchUp(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OM_WSGROUP_ID wsGroupID,
NET_UID userID);
//
//
// IssueSendDeny(...)
//
// This function issues a SEND_DENY message to a remote node.
//
//
void IssueSendDeny(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OM_WSGROUP_ID wsGroupID,
NET_UID sender,
OM_CORRELATOR remoteCorrelator);
//
//
// WSGRecordMove(...)
//
// This function moves the record for specified workset group from one
// Domain record to another, and posts events to all relevant Clients. If
// does not check for name contention in the destination Domain.
//
//
void WSGRecordMove(POM_PRIMARY pomPrimary,
POM_DOMAIN pDestDomainRec,
POM_WSGROUP pWSGroup);
//
//
// WSGMove(...)
//
// This function moves the record for specified workset group from one
// Domain record to another, and posts events to all relevant Clients. If
// does not check for name contention in the destination Domain.
//
//
UINT WSGMove(POM_PRIMARY pomPrimary,
POM_DOMAIN pDestDomainRec,
POM_WSGROUP pWSGroup);
//
//
// DomainAttach(...)
//
// This function calls MG_AttachUser to start the process of attaching to
// a Domain. It also allocates and initialises the local structures
// associated with the Domain (the Domain record).
//
// A pointer to the newly-created Domain record is returned.
//
//
UINT DomainAttach(POM_PRIMARY pomPrimary,
UINT callID,
POM_DOMAIN * ppDomainord);
//
//
// WSGRecordFindOrCreate(...)
//
// This function searches the workset group list in the specified Domain
// record for a workset group record whose name and FP match the ones
// specified. If none is found, a new workset group record is allocated,
// initialised and inserted into the list.
//
// A pointer to the found-or-created workset group record is returned.
//
// NOTE: This function does not cause ObMan to join the workset group
// channel or copy the workset group from another node if it
// exists elsewhere; it merely creates the local structures for
// the workset group.
//
//
UINT WSGRecordFindOrCreate(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OMWSG wsg,
OMFP fpHandler,
POM_WSGROUP * ppWSGroup);
//
//
// ProcessSendQueue(...)
//
// This function prompts ObMan to examine the send queues for the specified
// Domain. If there are any messages queued for sending (including remains
// of messages which have been partly sent), ObMan will try to send more
// data. ObMan stops when either the send queues are all empty or the
// network layer has stopped giving us memory.
//
// The <domainRecBumped> flag indicates whether the Domain record has had
// its use count bumped; if TRUE, then this function calls UT_SubFreeShared
// to decrement the use count.
//
//
void ProcessSendQueue(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
BOOL domainRecBumped);
//
// ProcessWSGDiscard(...)
//
void ProcessWSGDiscard(POM_PRIMARY pomPrimary, POM_WSGROUP pWSGroup);
//
// ProcessWSGMove(...)
//
UINT ProcessWSGMove(POM_PRIMARY pomPrimary, long moveCBOffset);
//
// ProcessNetTokenGrab(...)
//
UINT ProcessNetTokenGrab(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
NET_RESULT result);
//
// ProcessCMSTokenAssign(...)
//
void ProcessCMSTokenAssign(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
BOOL success,
NET_TOKEN_ID tokenID);
//
// ProcessNetTokenInhibit(...)
//
UINT ProcessNetTokenInhibit(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
NET_RESULT result);
//
// CreateObManControl(...)
//
UINT ObManControlInit(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain);
//
// WSGDiscard(...)
//
void WSGDiscard(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup,
BOOL fExit);
//
// IssueSendReq(...)
//
UINT IssueSendReq(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup,
NET_UID remoteNode);
//
// GenerateUnlockMessage(...)
//
UINT GenerateUnlockMessage(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OM_WSGROUP_ID wsGroupID,
OM_WORKSET_ID worksetID,
POMNET_LOCK_PKT * ppUnlockPkt);
//
// ProcessWSGRegister(...)
//
void ProcessWSGRegister(POM_PRIMARY pomPrimary, POM_WSGROUP_REG_CB pRegCB);
//
// LockObManControl(...)
//
void LockObManControl(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OM_CORRELATOR * pLockCorrelator);
//
//
// MaybeUnlockObManControl(...)
//
// If the LOCKED_OMC flag is set in the registration CB, then unlock the
// Obman Control workset and clear the LOCKED_OMC flag.
//
//
void MaybeUnlockObManControl(POM_PRIMARY pomPrimary,
POM_WSGROUP_REG_CB pRegistrationCB);
//
// WSGRecordCreate(...)
//
UINT WSGRecordCreate(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
OMWSG wsg,
OMFP fpHandler,
POM_WSGROUP * ppWSGroup);
//
//
// WorksetDiscard(...)
//
// This function is called by WSGDiscard to discard the individual worksets
// of a workset group when the last local Client deregisters. It discards
// the contents of the workset, frees the workset record itself and clears
// the worksets's entry in the workset group record.
//
// It is not called when a workset is closed, since closing a workset does
// not discard its contents.
//
//
void WorksetDiscard(POM_WSGROUP pWSGroup, POM_WORKSET * pWorkset, BOOL fExit);
//
// ProcessLockNotify(...)
//
void ProcessLockNotify(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
NET_UID owner);
//
// SendMessagePkt(...)
//
UINT SendMessagePkt(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_SEND_INST pSendInst);
//
// SendMoreData(...)
//
UINT SendMoreData(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_SEND_INST pSendInst);
//
// StartReceive(...)
//
UINT StartReceive(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POMNET_OPERATION_PKT pHeaderPkt,
POM_WSGROUP pWSGroup,
POM_WORKSET pWorkset,
POM_OBJECT pObj);
//
// ProcessMessage(...)
//
// This function takes a receive control block (generated by ReceiveData)
// and tries to process it as an ObMan message. If the message can not be
// processed at this time, it is put on the bounce list. If the message is
// an "enabling" message (one which might enable previously bounced
// messages to be processed now) the bounce queue is flushed.
//
// Since this function is also called to process bounced messages, and
// since we want to prevent deep recursion as one bounced "enabling"
// message prompts re-examination of the bounce queue etc., we use the
// <whatNext> parameter to determine whether the bounce list should be
// examined.
//
//
UINT ProcessMessage(POM_PRIMARY pomPrimary,
POM_RECEIVE_CB pReceiveCB,
UINT whatNext);
#define OK_TO_RETRY_BOUNCE_LIST 1
#define DONT_RETRY_BOUNCE_LIST 2
//
// ReceiveData(...)
//
UINT ReceiveData(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
PNET_SEND_IND_EVENT pNetSendInd,
POMNET_OPERATION_PKT pNetMessage);
//
// TryToSpoilOp
//
UINT TryToSpoilOp(POM_SEND_INST pSendInst);
//
// DecideTransferSize(...)
//
void DecideTransferSize(POM_SEND_INST pSendInst,
UINT * pTransferSize,
UINT * pDataTransferSize);
//
// CreateReceiveCB(...)
//
UINT CreateReceiveCB(POM_DOMAIN pDomain,
PNET_SEND_IND_EVENT pNetSendInd,
POMNET_OPERATION_PKT pNetMessage,
POM_RECEIVE_CB * ppReceiveCB);
//
// FindReceiveCB(...)
//
UINT FindReceiveCB(POM_DOMAIN pDomain,
PNET_SEND_IND_EVENT pNetSendInd,
POMNET_OPERATION_PKT pDataPkt,
POM_RECEIVE_CB * ppReceiveCB);
//
// WSGRegisterAbort(...)
//
void WSGRegisterAbort(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain,
POM_WSGROUP_REG_CB pRegistrationCB);
//
//
// BounceMessage(...)
//
// cmf
//
//
void BounceMessage(POM_DOMAIN pDomain,
POM_RECEIVE_CB pReceiveCB);
//
//
// NewDomainRecord(...)
//
//
UINT NewDomainRecord(POM_PRIMARY pomPrimary, UINT callID, POM_DOMAIN * ppDomain);
void FreeDomainRecord(POM_DOMAIN * ppDomain);
//
// ProcessBouncedMessages(...)
//
void ProcessBouncedMessages(POM_PRIMARY pomPrimary,
POM_DOMAIN pDomain);
void WSGResetBytesUnacked(POM_WSGROUP pWSGroup);
//
// NewHelperCB()
// FreeHelperCB()
//
BOOL NewHelperCB(POM_DOMAIN pDomain,
POM_WSGROUP pWSGroup,
NET_UID lateJoiner,
OM_CORRELATOR remoteCorrelator,
POM_HELPER_CB * ppHelperCB);
void FreeHelperCB(POM_HELPER_CB * ppHelperCB);
void PurgePendingOps(POM_WORKSET pWorkset, POM_OBJECT pObj);
OMFP OMMapNameToFP(LPCSTR szName);
LPCSTR OMMapFPToName(OMFP fp);
OMWSG OMMapNameToWSG(LPCSTR szName);
LPCSTR OMMapWSGToName(OMWSG wsg);
#endif // _H_OM