#ifndef VRPN_MUTEX_H
#define VRPN_MUTEX_H
#include "vrpn_Shared.h" // for timeval and timeval manipulation functions
#include "vrpn_Connection.h" // for vrpn_HANDLERPARAM
// Every time a Mutex_Remote connects to a Mutex_Server, the server assigns
// a unique ID to the remote.
// HACK - because vrpn doesn't let us unicast within a multicast (multiple-
// connection server) (in any clean way), or identify at a MC server which
// connection a message came in over, this code is fragile - it depends
// on the fact that vrpn_Connection only allows one connection to be made
// before triggering the got_connection callback. If connections were somehow
// batched, or we multithreaded vrpn_Connection, this would break.
class vrpn_Mutex {
public:
vrpn_Mutex (const char * name, vrpn_Connection * = NULL);
virtual ~vrpn_Mutex (void) = 0;
void mainloop (void);
protected:
vrpn_Connection * d_connection;
vrpn_int32 d_myId;
vrpn_int32 d_requestIndex_type;
vrpn_int32 d_requestMutex_type;
vrpn_int32 d_release_type;
vrpn_int32 d_releaseNotification_type;
vrpn_int32 d_grantRequest_type;
vrpn_int32 d_denyRequest_type;
vrpn_int32 d_initialize_type;
void sendRequest (vrpn_int32 index);
void sendRelease (void);
void sendReleaseNotification (void);
void sendGrantRequest (vrpn_int32 index);
void sendDenyRequest (vrpn_int32 index);
};
class vrpn_Mutex_Server : public vrpn_Mutex {
public:
vrpn_Mutex_Server (const char * name, vrpn_Connection * = NULL);
virtual ~vrpn_Mutex_Server (void);
protected:
enum state { HELD, FREE };
state d_state;
vrpn_int32 d_remoteIndex;
static int handle_requestIndex (void *, vrpn_HANDLERPARAM);
static int handle_requestMutex (void *, vrpn_HANDLERPARAM);
static int handle_release (void *, vrpn_HANDLERPARAM);
static int handle_gotConnection (void *, vrpn_HANDLERPARAM);
static int handle_dropLastConnection (void *, vrpn_HANDLERPARAM);
};
class vrpn_Mutex_Remote : public vrpn_Mutex {
public:
vrpn_Mutex_Remote (const char * name, vrpn_Connection * = NULL);
virtual ~vrpn_Mutex_Remote (void);
// ACCESSORS
vrpn_bool isAvailable (void) const;
vrpn_bool isHeldLocally (void) const;
vrpn_bool isHeldRemotely (void) const;
// MANIPULATORS
void request (void);
void release (void);
void addRequestGrantedCallback (void * userdata, int (*) (void *));
void addRequestDeniedCallback (void * userdata, int (*) (void *));
void addTakeCallback (void * userdata, int (*) (void *));
void addReleaseCallback (void * userdata, int (*) (void *));
protected:
void requestIndex (void);
enum state { OURS, REQUESTING, AVAILABLE, HELD_REMOTELY};
state d_state;
vrpn_int32 d_myIndex;
vrpn_bool d_requestBeforeInit;
static int handle_grantRequest (void *, vrpn_HANDLERPARAM);
static int handle_denyRequest (void *, vrpn_HANDLERPARAM);
static int handle_releaseNotification (void *, vrpn_HANDLERPARAM);
static int handle_initialize (void *, vrpn_HANDLERPARAM);
static int handle_gotConnection (void *, vrpn_HANDLERPARAM);
void triggerGrantCallbacks (void);
void triggerDenyCallbacks (void);
void triggerTakeCallbacks (void);
void triggerReleaseCallbacks (void);
struct mutexCallback {
int (* f) (void *);
void * userdata;
mutexCallback * next;
};
mutexCallback * d_reqGrantedCB;
mutexCallback * d_reqDeniedCB;
mutexCallback * d_takeCB;
mutexCallback * d_releaseCB;
};
// Known bugs -
// The constructor that takes a Connection as an argument will incorrectly
// identify its IP address as the machine's default rather than the address
// used by the Connection. This should not cause any errors in the protocol,
// but will bias the tiebreaking algorithm. The same constructor will use
// the wrong port number; without this information the tiebreaking algorithm
// fails. Oops. Use only one mutex per Connection for now.
// Possible bugs -
// If on startup somebody else is holding the mutex we'll think it's
// available. However, if we request it they'll deny it to us and
// we won't break.
// If sites don't execute the same set of addPeer() commands, they may
// implicitly partition the network and not get true mutual exclusion.
// This could be fixed by sending an addPeer message.
// If sites execute addPeer() while the lock is held, or being requested,
// we'll break.
// - To fix: send messages, but defer all executions of addPeer until the
// lock is released. If we want to be really careful here, on getting an
// addPeer message when we think the lock is available we should request
// the lock and then (if we get it) release it immediately, without
// triggering any user callbacks. Sounds tough to code?
// Handling more than 2 sites in a mutex requires multiconnection servers.
// It's been tested with 1-3 sites, and works fine.
// This is an O(n^2) network traffic implementation;
// for details (and how to fix if it ever becomes a problem),
// see the implementation notes in vrpn_Mutex.C.
class vrpn_PeerMutex {
public:
vrpn_PeerMutex (const char * name, int port,
const char * NICaddress = NULL);
~vrpn_PeerMutex (void);
// ACCESSORS
vrpn_bool isAvailable (void) const;
vrpn_bool isHeldLocally (void) const;
vrpn_bool isHeldRemotely (void) const;
int numPeers (void) const;
// MANIPULATORS
void mainloop (void);
void request (void);
void release (void);
void addPeer (const char * stationName);
void addRequestGrantedCallback (void * userdata, int (*) (void *));
void addRequestDeniedCallback (void * userdata, int (*) (void *));
void addTakeCallback (void * userdata, int (*) (void *));
void addReleaseCallback (void * userdata, int (*) (void *));
protected:
enum state { OURS, REQUESTING, AVAILABLE, HELD_REMOTELY};
char * d_mutexName;
state d_state;
int d_numPeersGrantingLock;
vrpn_Connection * d_server;
vrpn_Connection ** d_peer;
int d_numPeers;
int d_numConnectionsAllocated;
vrpn_uint32 d_myIP;
vrpn_uint32 d_myPort;
vrpn_uint32 d_holderIP;
vrpn_int32 d_holderPort;
vrpn_int32 d_myId;
vrpn_int32 d_request_type;
vrpn_int32 d_release_type;
vrpn_int32 d_grantRequest_type;
vrpn_int32 d_denyRequest_type;
//vrpn_int32 d_losePeer_type;
static int handle_request (void *, vrpn_HANDLERPARAM);
static int handle_release (void *, vrpn_HANDLERPARAM);
static int handle_grantRequest (void *, vrpn_HANDLERPARAM);
static int handle_denyRequest (void *, vrpn_HANDLERPARAM);
static int handle_losePeer (void *, vrpn_HANDLERPARAM);
void sendRequest (vrpn_Connection *);
void sendRelease (vrpn_Connection *);
void sendGrantRequest (vrpn_Connection *, vrpn_uint32 IPnumber,
vrpn_uint32 PortNumber);
void sendDenyRequest (vrpn_Connection *, vrpn_uint32 IPnumber,
vrpn_uint32 PortNumber);
void triggerGrantCallbacks (void);
void triggerDenyCallbacks (void);
void triggerTakeCallbacks (void);
void triggerReleaseCallbacks (void);
void checkGrantMutex (void);
void init (const char * name);
struct mutexCallback {
int (* f) (void *);
void * userdata;
mutexCallback * next;
};
mutexCallback * d_reqGrantedCB;
mutexCallback * d_reqDeniedCB;
mutexCallback * d_takeCB;
mutexCallback * d_releaseCB;
struct peerData {
vrpn_uint32 IPaddress;
vrpn_uint32 port;
vrpn_bool grantedLock;
};
peerData * d_peerData;
vrpn_PeerMutex (const char * name, vrpn_Connection * c);
};
#endif // VRPN_MUTEX_H