#include "normApi.h" #include "normSession.h" #ifdef WIN32 #ifndef _WIN32_WCE #include // for _mktemp() #endif // !_WIN32_WCE #endif // WIN32 // const defs const NormInstanceHandle NORM_INSTANCE_INVALID = ((NormInstanceHandle)0); const NormSessionHandle NORM_SESSION_INVALID = ((NormSessionHandle)0); const NormNodeHandle NORM_NODE_INVALID = ((NormNodeHandle)0); const NormNodeId NORM_NODE_NONE = ((NormNodeId)0x00000000); const NormNodeId NORM_NODE_ANY = ((NormNodeId)0xffffffff); const NormObjectHandle NORM_OBJECT_INVALID = ((NormObjectHandle)0); const NormDescriptor NORM_DESCRIPTOR_INVALID = ProtoDispatcher::INVALID_DESCRIPTOR; /** The "NormInstance" class is a C++ helper class that keeps * state for an instance of the NORM API. It acts as a * "go between" the API's procedural function calls and * the underlying NORM C++ implementation */ class NormInstance : public NormController { public: NormInstance(); virtual ~NormInstance(); void Notify(NormController::Event event, class NormSessionMgr* sessionMgr, class NormSession* session, class NormServerNode* server, class NormObject* object); bool Startup(bool priorityBoost = false); void Shutdown(); bool WaitForEvent(); bool GetNextEvent(NormEvent* theEvent); bool SetCacheDirectory(const char* cachePath); bool NotifyQueueIsEmpty() const {return notify_queue.IsEmpty();} void PurgeObjectNotifications(NormObjectHandle objectHandle); ProtoDispatcher::Descriptor GetDescriptor() const { #ifdef WIN32 return notify_event; #else return notify_fd[0]; #endif // if/else WIN32/UNIX } static NormInstance* GetInstanceFromSession(NormSessionHandle sessionHandle) { NormSession* session = (NormSession*)sessionHandle; NormInstance* theInstance = static_cast(session->GetSessionMgr().GetController()); return theInstance; //return static_cast(session->GetSessionMgr().GetController()); } static NormInstance* GetInstanceFromNode(NormNodeHandle nodeHandle) { NormSession& session = ((NormNode*)nodeHandle)->GetSession(); return static_cast(session.GetSessionMgr().GetController()); } static NormInstance* GetInstanceFromObject(NormObjectHandle objectHandle) { NormSession& session = ((NormObject*)objectHandle)->GetSession();; return static_cast(session.GetSessionMgr().GetController()); } class Notification { public: NormEvent event; void Append(Notification* n) {next = n;} Notification* GetNext() const {return next;} class Queue { public: Queue(); ~Queue(); bool IsEmpty() const {return (NULL == head);} void Destroy(); void Append(Notification* n) { n->Append(NULL); if (tail) tail->Append(n); else head = n; tail = n; } Notification* RemoveHead() { Notification* n = head; if (n) { head = n->GetNext(); tail = head ? tail : NULL; } return n; } Notification* GetHead() {return head;} void SetHead(Notification* n) {head = n;} private: Notification* head; Notification* tail; }; // end class NormInstance::Notification::Queue private: Notification* next; }; // end class NormInstance::Notification ProtoDispatcher dispatcher; NormSessionMgr session_mgr; private: Notification::Queue notify_pool; Notification::Queue notify_queue; Notification* previous_notification; const char* rx_cache_path; #ifdef WIN32 HANDLE notify_event; #else int notify_fd[2]; #endif // if/else WIN32/UNIX }; // end class NormInstance //////////////////////////////////////////////////// // NormInstance::Notification::Queue implementation NormInstance::Notification::Queue::Queue() : head(NULL), tail(NULL) { } NormInstance::Notification::Queue::~Queue() { Destroy(); } void NormInstance::Notification::Queue::Destroy() { Notification* n; while ((n = RemoveHead())) delete n; } // end NormInstance::Notification::Queue::Destroy() //////////////////////////////////////////////////// // NormInstance implementation NormInstance::NormInstance() : session_mgr(static_cast(dispatcher), static_cast(dispatcher)), previous_notification(NULL), rx_cache_path(NULL) { #ifdef WIN32 notify_event = NULL; #else notify_fd[0] = notify_fd[1] = -1; #endif // if/else WIN32/UNIX session_mgr.SetController(this); } NormInstance::~NormInstance() { Shutdown(); } bool NormInstance::SetCacheDirectory(const char* cachePath) { // (TBD) verify that we can _write_ to this directory! bool result = false; if (dispatcher.SuspendThread()) { int length = strlen(cachePath); if (PROTO_PATH_DELIMITER != cachePath[length-1]) length += 2; else length += 1; length = (length < PATH_MAX) ? length : PATH_MAX; char* pathStorage = new char[length]; if (pathStorage) { strncpy(pathStorage, cachePath, length); pathStorage[length - 2] = PROTO_PATH_DELIMITER; pathStorage[length - 1] = '\0'; if (rx_cache_path) delete[] (char*)rx_cache_path; rx_cache_path = pathStorage; result = true; } else { DMSG(0, "NormInstance::SetCacheDirectory() new pathStorage error: %s\n", GetErrorString()); } dispatcher.ResumeThread(); } return result; } // end NormInstance::SetCacheDirectory() void NormInstance::Notify(NormController::Event event, class NormSessionMgr* sessionMgr, class NormSession* session, class NormServerNode* server, class NormObject* object) { // (TBD) set a limit on how many pending notifications // we allow to queue up (it could be large and probably // we could base it on how much memory space the pending // notifications are allowed to consume. Notification* n = notify_pool.RemoveHead(); if (!n) { if (!(n = new Notification)) { DMSG(0, "NormInstance::Notify() new Notification error: %s\n", GetErrorString()); return; } } switch (event) { case RX_OBJECT_NEW: { // new recv object "Accept()" policy implemented here switch (object->GetType()) { case NormObject::STREAM: { NormStreamObject* stream = static_cast(object); // (TBD) implement silent_client accept differently NormObjectSize size = stream->GetSize(); // We double the stream buffer to prevent unecessary data loss // for our threaded API if (!stream->Accept(size.LSB(), true)) { DMSG(0, "NormInstance::Notify() stream accept error\n"); notify_pool.Append(n); return; } // By setting a non-zero "block pool threshold", this // gives the API a chance to "catch up" on reading // when the receive stream becomes buffer-constrained UINT32 blockPoolCount = stream->GetBlockPoolCount(); stream->SetBlockPoolThreshold(blockPoolCount / 2); break; } case NormObject::FILE: { if (NULL != rx_cache_path) { char fileName[PATH_MAX]; strncpy(fileName, rx_cache_path, PATH_MAX); size_t catMax = strlen(fileName); if (catMax > PATH_MAX) catMax = 0; else catMax = PATH_MAX - catMax; strncat(fileName, "normTempXXXXXX", catMax); #ifdef WIN32 #ifdef _WIN32_WCE bool tempFileOK = false; for (int i = 0; i < 255; i++) { strncpy(fileName, rx_cache_path, PATH_MAX); catMax = strlen(fileName); if (catMax > PATH_MAX) catMax = 0; else catMax = PATH_MAX - catMax; strncat(fileName, "normTempXXXXXX", catMax); char tempName[16]; sprintf(tempName, "normTemp%06u", i); strcat(fileName, tempName); if(!NormFile::IsLocked(fileName)) { tempFileOK = true; break; } } if (!tempFileOK) #else if (!_mktemp(fileName)) #endif // if/else _WIN32_WCE #else int fd = mkstemp(fileName); if (fd >= 0) { close(fd); } else #endif // if/else WIN32 { DMSG(0, "NormInstance::Notify(RX_OBJECT_NEW) Warning: mkstemp() error: %s\n", GetErrorString()); } if (!static_cast(object)->Accept(fileName)) { DMSG(0, "NormInstance::Notify(RX_OBJECT_NEW) file object accept error!\n"); } } else { // we're ignoring files return; } break; } case NormObject::DATA: { NormDataObject* dataObj = static_cast(object); unsigned int dataLen = (unsigned int)(object->GetSize().GetOffset()); char* dataPtr = new char[dataLen]; if (NULL == dataPtr) { DMSG(0, "NormInstance::Notify(RX_OBJECT_NEW) new dataPtr error: %s\n", GetErrorString()); return; } // Note that the "true" parameter means the // NORM protocol engine will free the allocated // data on object deletion, so the app should // use NormDataDetachData() to keep the received // data (or copy it before the data object is deleted) dataObj->Accept(dataPtr, dataLen, true); break; } default: // This shouldn't occur return; } // end switch(object->GetType()) break; } // end case RX_OBJECT_NEW case RX_OBJECT_COMPLETED: case TX_OBJECT_PURGED: case RX_OBJECT_ABORTED: object->Retain(); break; default: break; } // end switch(event) bool doNotify = notify_queue.IsEmpty(); n->event.type = (NormEventType)event; n->event.session = session; n->event.sender = server; n->event.object = object; notify_queue.Append(n); if (doNotify) { #ifdef WIN32 if (0 == SetEvent(notify_event)) { DMSG(0, "NormInstance::Notify() SetEvent() error: %s\n", GetErrorString()); } #else char byte; while (1 != write(notify_fd[1], &byte, 1)) { if ((EINTR != errno) && (EAGAIN != errno)) { DMSG(0, "NormInstance::Notify() write() error: %s\n", GetErrorString()); break; } } #endif // if/else WIN32/UNIX } } // end NormInstance::Notify() void NormInstance::PurgeObjectNotifications(NormObjectHandle objectHandle) { Notification* next = notify_queue.GetHead(); while (next) { // TBD - return these to the pool? if (objectHandle == next->event.object) { switch (next->event.type) { case RX_OBJECT_COMPLETED: case TX_OBJECT_PURGED: case RX_OBJECT_ABORTED: ((NormObject*)(next->event.object))->Release(); break; default: break; } next->event.type = NORM_EVENT_INVALID; } next = next->GetNext(); } // (TBD) if we put these in the pool, should we check that the queue has been emptied // and possible reset event/fd } // end NormInstance::PurgeObjectNotifications() // NormInstance::dispatcher MUST be suspended _before_ calling this bool NormInstance::GetNextEvent(NormEvent* theEvent) { // First, do any garbage collection for "previous_notification" if (NULL != previous_notification) { // "Release" any purged/completed/aborted objects switch(previous_notification->event.type) { case NORM_RX_OBJECT_COMPLETED: case NORM_TX_OBJECT_PURGED: case NORM_RX_OBJECT_ABORTED: { NormObject* obj = (NormObject*)(previous_notification->event.object); obj->Release(); break; } default: break; } notify_pool.Append(previous_notification); previous_notification = NULL; } Notification* n; while ((n = notify_queue.RemoveHead())) { if (notify_queue.IsEmpty()) { #ifdef WIN32 if (0 == ResetEvent(notify_event)) { DMSG(0, "NormInstance::GetNextEvent() ResetEvent error: %s\n", GetErrorString()); } #else char byte[32]; while (read(notify_fd[0], byte, 32) > 0); // TBD - error check #endif // if/else WIN32/UNIX } switch (n->event.type) { case NORM_EVENT_INVALID: continue; case NORM_RX_OBJECT_UPDATED: // reset update event notification ((NormObject*)n->event.object)->SetNotifyOnUpdate(true); break; case NORM_TX_QUEUE_VACANCY: if (NULL != n->event.object) static_cast((NormObject*)n->event.object)->ResetPostedTxQueueVacancy(); // (TBD) support session-level queue vacancy notify break; default: break; } if (theEvent) *theEvent = n->event; previous_notification = n; // keep dispatched event for garbage collection return true; } return false; } // end NormInstance::GetNextEvent() bool NormInstance::WaitForEvent() { #ifdef WIN32 WaitForSingleObject(notify_event, INFINITE); #else fd_set fdSet; FD_ZERO(&fdSet); FD_SET(notify_fd[0], &fdSet); while (1) { if (0 > select(notify_fd[0] + 1, &fdSet, (fd_set*)NULL, (fd_set*)NULL, (struct timeval*)NULL)) { if (EINTR != errno) { DMSG(0, "NormInstance::WaitForEvent() select() error: %s\n", GetErrorString()); return false; } } else { break; } } #endif return true; } // end NormInstance::WaitForEvent() bool NormInstance::Startup(bool priorityBoost) { // 1) Create descriptor to use for event notification #ifdef WIN32 // Create initially non-signalled, manual reset event notify_event = CreateEvent(NULL, TRUE, FALSE, NULL); if (NULL == notify_event) { DMSG(0, "NormInstance::Startup() CreateEvent() error: %s\n", GetErrorString()); return false; } #else if (0 != pipe(notify_fd)) { DMSG(0, "NormInstance::Startup() pipe() error: %s\n", GetErrorString()); return false; } // make reading non-blocking if(-1 == fcntl(notify_fd[0], F_SETFL, fcntl(notify_fd[0], F_GETFL, 0) | O_NONBLOCK)) { DMSG(0, "NormInstance::Startup() fcntl(F_SETFL(O_NONBLOCK)) error: %s\n", GetErrorString()); close(notify_fd[0]); close(notify_fd[1]); notify_fd[0] = notify_fd[1] = -1; return false; } #endif // if/else WIN32/UNIX // 2) Start thread return dispatcher.StartThread(priorityBoost); } // end NormInstance::Startup() void NormInstance::Shutdown() { dispatcher.Stop(); #ifdef WIN32 if (NULL != notify_event) { CloseHandle(notify_event); notify_event = NULL; } #else if (notify_fd[0] > 0) { close(notify_fd[0]); // close read end of pipe close(notify_fd[1]); // close write end of pipe notify_fd[0] = notify_fd[1] = -1; } #endif // if/else WIN32/UNIX if (rx_cache_path) { delete[] (char*)rx_cache_path; rx_cache_path = NULL; } // Garbage collect our "previous_notification" if (NULL != previous_notification) { switch(previous_notification->event.type) { case NORM_RX_OBJECT_COMPLETED: case NORM_TX_OBJECT_PURGED: case NORM_RX_OBJECT_ABORTED: { NormObject* obj = (NormObject*)(previous_notification->event.object); obj->Release(); break; } default: break; } notify_pool.Append(previous_notification); previous_notification = NULL; } Notification* n; while ((n = notify_queue.RemoveHead())) { switch (n->event.type) { case NORM_RX_OBJECT_NEW: { NormObject* obj = (NormObject*)n->event.object; switch (obj->GetType()) { case NormObject::FILE: // (TBD) unlink temp file? break; default: break; } break; } // Garbage collect retained objects case NORM_RX_OBJECT_COMPLETED: case NORM_TX_OBJECT_PURGED: case NORM_RX_OBJECT_ABORTED: ((NormObject*)n->event.object)->Release(); break; default: break; } delete n; } notify_pool.Destroy(); } // end NormInstance::Shutdown() ////////////////////////////////////////////////////////////////////////// // NORM API FUNCTION IMPLEMENTATIONS // /** NORM API Initialization */ NormInstanceHandle NormCreateInstance(bool priorityBoost) { NormInstance* normInstance = new NormInstance; if (normInstance) { if (normInstance->Startup(priorityBoost)) return ((NormInstanceHandle)normInstance); else delete normInstance; } return NORM_INSTANCE_INVALID; } // end NormCreateInstance() void NormDestroyInstance(NormInstanceHandle instanceHandle) { delete ((NormInstance*)instanceHandle); } // end NormDestroyInstance() bool NormSetCacheDirectory(NormInstanceHandle instanceHandle, const char* cachePath) { NormInstance* instance = (NormInstance*)instanceHandle; if (instance) return instance->SetCacheDirectory(cachePath); else return false; } // end NormSetCacheDirectory() NormDescriptor NormGetDescriptor(NormInstanceHandle instanceHandle) { NormInstance* instance = (NormInstance*)instanceHandle; if (instance) return (instance->GetDescriptor()); else return NORM_DESCRIPTOR_INVALID; } // end NormGetDescriptor() // (TBD) add a timeout option to this? bool NormGetNextEvent(NormInstanceHandle instanceHandle, NormEvent* theEvent) { bool result = false; NormInstance* instance = (NormInstance*)instanceHandle; if (instance) { if (instance->NotifyQueueIsEmpty()) //(TBD) perform this check??? instance->WaitForEvent(); if (instance->dispatcher.SuspendThread()) { result = instance->GetNextEvent(theEvent); instance->dispatcher.ResumeThread(); } } return result; } // end NormGetNextEvent() /** NORM Session Creation and Control Functions */ NormSessionHandle NormCreateSession(NormInstanceHandle instanceHandle, const char* sessionAddr, unsigned short sessionPort, NormNodeId localNodeId) { // (TBD) wrap this with SuspendThread/ResumeThread ??? NormInstance* instance = (NormInstance*)instanceHandle; if (instance) { NormSession* session = instance->session_mgr.NewSession(sessionAddr, sessionPort, localNodeId); if (session) return ((NormSessionHandle)session); } return NORM_SESSION_INVALID; } // end NormCreateSession() void NormDestroySession(NormSessionHandle sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) { session->Close(); session->GetSessionMgr().DeleteSession(session); } instance->dispatcher.ResumeThread(); } } } // end NormDestroySession() void NormSetUserData(NormSessionHandle sessionHandle, const void* userData) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetUserData(userData); instance->dispatcher.ResumeThread(); } } } // end NormSetUserData() const void* NormGetUserData(NormSessionHandle sessionHandle) { const void* userData = NULL; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) userData = session->GetUserData(); instance->dispatcher.ResumeThread(); } } return userData; } // end NormGetUserData() NormNodeId NormGetLocalNodeId(NormSessionHandle sessionHandle) { NormSession* session = (NormSession*)sessionHandle; if (NULL != session) return session->LocalNodeId(); else return NORM_NODE_NONE; } // end NormGetLocalNodeId() void NormSetTxPort(NormSessionHandle sessionHandle, unsigned short txPort) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetTxPort(txPort); instance->dispatcher.ResumeThread(); } } } // end NormSetTxPort() bool NormSetMulticastInterface(NormSessionHandle sessionHandle, const char* interfaceName) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->SetMulticastInterface(interfaceName); instance->dispatcher.ResumeThread(); } } return result; } // end NormSetMulticastInterface() bool NormSetTTL(NormSessionHandle sessionHandle, unsigned char ttl) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->SetTTL(ttl); instance->dispatcher.ResumeThread(); } } return result; } // end NormSetTTL() bool NormSetTOS(NormSessionHandle sessionHandle, unsigned char tos) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->SetTTL(tos); instance->dispatcher.ResumeThread(); } } return result; } // end NormSetTOS() /** Special test/debug support functions */ bool NormSetLoopback(NormSessionHandle sessionHandle, bool state) { NormSession* session = (NormSession*)sessionHandle; if (session) return session->SetLoopback(state); else return false; } // end NormSetLoopback() void NormSetMessageTrace(NormSessionHandle sessionHandle, bool state) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetTrace(state); } // end NormSetMessageTrace() void NormSetTxLoss(NormSessionHandle sessionHandle, double percent) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetTxLoss(percent); } // end NormSetTxLoss() void NormSetRxLoss(NormSessionHandle sessionHandle, double percent) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetRxLoss(percent); } // end NormSetRxLoss() /** NORM Sender Functions */ bool NormStartSender(NormSessionHandle sessionHandle, unsigned long bufferSpace, unsigned short segmentSize, unsigned char numData, unsigned char numParity) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->StartServer(bufferSpace, segmentSize, numData, numParity); else result = false; instance->dispatcher.ResumeThread(); } return result; } // end NormStartSender() void NormStopSender(NormSessionHandle sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->StopServer(); instance->dispatcher.ResumeThread(); } } // end NormStopSender() void NormSetTransmitRate(NormSessionHandle sessionHandle, double bitsPerSecond) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetTxRate(bitsPerSecond); instance->dispatcher.ResumeThread(); } } // end NormSetTransmitRate() bool NormSetTxSocketBuffer(NormSessionHandle sessionHandle, unsigned int bufferSize) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->SetTxSocketBuffer(bufferSize); instance->dispatcher.ResumeThread(); } return result; } // end NormSetTxSocketBuffer() void NormSetCongestionControl(NormSessionHandle sessionHandle, bool enable) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetCongestionControl(enable); instance->dispatcher.ResumeThread(); } } // end NormSetCongestionControl() void NormSetTransmitRateBounds(NormSessionHandle sessionHandle, double rateMin, double rateMax) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetTxRateBounds(rateMin, rateMax); instance->dispatcher.ResumeThread(); } } // end NormSetTransmitRateBounds() void NormSetAutoParity(NormSessionHandle sessionHandle, unsigned char autoParity) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ServerSetAutoParity(autoParity); instance->dispatcher.ResumeThread(); } } // end NormSetAutoParity() void NormSetGrttEstimate(NormSessionHandle sessionHandle, double grttEstimate) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ServerSetGrtt(grttEstimate); instance->dispatcher.ResumeThread(); } } // end NormSetGrttEstimate() bool NormAddAckingNode(NormSessionHandle sessionHandle, NormNodeId nodeId) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->ServerAddAckingNode(nodeId); instance->dispatcher.ResumeThread(); } return result; } // end NormAddAckingNode() void NormRemoveAckingNode(NormSessionHandle sessionHandle, NormNodeId nodeId) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ServerRemoveAckingNode(nodeId); instance->dispatcher.ResumeThread(); } } // end NormRemoveAckingNode() NormObjectHandle NormFileEnqueue(NormSessionHandle sessionHandle, const char* fileName, const char* infoPtr, unsigned int infoLen) { NormObjectHandle objectHandle = NORM_OBJECT_INVALID; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) { NormObject* obj = session->QueueTxFile(fileName, infoPtr, infoLen); if (obj) objectHandle = (NormObjectHandle)obj; } instance->dispatcher.ResumeThread(); } return objectHandle; } // end NormFileEnqueue() NormObjectHandle NormDataEnqueue(NormSessionHandle sessionHandle, const char* dataPtr, unsigned long dataLen, const char* infoPtr = NULL, unsigned int infoLen = 0) { NormObjectHandle objectHandle = NORM_OBJECT_INVALID; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) { NormObject* obj = session->QueueTxData(dataPtr, dataLen, infoPtr, infoLen); if (obj) objectHandle = (NormObjectHandle)obj; } instance->dispatcher.ResumeThread(); } return objectHandle; } // end NormDataEnqueue() NormObjectHandle NormStreamOpen(NormSessionHandle sessionHandle, unsigned long bufferSize) { NormObjectHandle objectHandle = NORM_OBJECT_INVALID; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) { NormObject* obj = static_cast(session->QueueTxStream(bufferSize, true)); if (obj) objectHandle = (NormObjectHandle)obj; } instance->dispatcher.ResumeThread(); } return objectHandle; } // end NormStreamOpen() void NormStreamClose(NormObjectHandle streamHandle, bool graceful) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (NULL != stream) { if (graceful && (NULL == stream->GetServer())) { NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if (instance && instance->dispatcher.SuspendThread()) { stream->Close(true); // graceful stream closure instance->dispatcher.ResumeThread(); } } else { NormObjectCancel(streamHandle); } } } // end NormStreamClose() unsigned int NormStreamWrite(NormObjectHandle streamHandle, const char* buffer, unsigned int numBytes) { unsigned int result = 0; NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if (instance && instance->dispatcher.SuspendThread()) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (stream) result = stream->Write(buffer, numBytes, false); instance->dispatcher.ResumeThread(); } return result; } // end NormStreamWrite() void NormStreamFlush(NormObjectHandle streamHandle, bool eom, NormFlushMode flushMode) { NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if (instance && instance->dispatcher.SuspendThread()) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (stream) { NormStreamObject::FlushMode saveFlushMode = stream->GetFlushMode(); stream->SetFlushMode((NormStreamObject::FlushMode)flushMode); stream->Flush(eom); stream->SetFlushMode(saveFlushMode); } instance->dispatcher.ResumeThread(); } } // end NormStreamFlush() void NormStreamSetAutoFlush(NormObjectHandle streamHandle, NormFlushMode flushMode) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (stream) stream->SetFlushMode((NormStreamObject::FlushMode)flushMode); } // end NormStreamSetAutoFlush() void NormStreamSetPushEnable(NormObjectHandle streamHandle, bool state) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (stream) stream->SetPushMode(state); } // end NormStreamSetPushEnable() bool NormStreamHasVacancy(NormObjectHandle streamHandle) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if (instance && instance->dispatcher.SuspendThread()) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (NULL != stream) result = stream->HasVacancy(); instance->dispatcher.ResumeThread(); } return result; } // end NormStreamHasVacancy() void NormStreamMarkEom(NormObjectHandle streamHandle) { NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if (instance && instance->dispatcher.SuspendThread()) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (stream) stream->Write(NULL, 0, true); instance->dispatcher.ResumeThread(); } } // end NormStreamMarkEom() bool NormSetWatermark(NormSessionHandle sessionHandle, NormObjectHandle objectHandle) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; NormObject* obj = (NormObject*)objectHandle; if (session && obj) { // (segmentId doesn't matter for non-stream) if (obj->IsStream()) { NormStreamObject* stream = static_cast(obj); session->ServerSetWatermark(stream->GetId(), stream->FlushBlockId(), stream->FlushSegmentId()); } else { NormBlockId blockId = obj->GetFinalBlockId(); NormSegmentId segmentId = obj->GetBlockSize(blockId) - 1; session->ServerSetWatermark(obj->GetId(), blockId, segmentId); } result = true; } instance->dispatcher.ResumeThread(); } return result; } // end NormSetWatermark() /** NORM Receiver Functions */ bool NormStartReceiver(NormSessionHandle sessionHandle, unsigned long bufferSpace) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->StartClient(bufferSpace); else result = false; instance->dispatcher.ResumeThread(); } return result; } // end NormStartReceiver() void NormStopReceiver(NormSessionHandle sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->StopClient(); instance->dispatcher.ResumeThread(); } } // end NormStopReceiver() bool NormSetRxSocketBuffer(NormSessionHandle sessionHandle, unsigned int bufferSize) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->SetRxSocketBuffer(bufferSize); instance->dispatcher.ResumeThread(); } return result; } // end NormSetRxSocketBuffer() void NormSetSilentReceiver(NormSessionHandle sessionHandle, bool silent) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ClientSetSilent(silent); } // end NormSetSilentReceiver() void NormSetDefaultUnicastNack(NormSessionHandle sessionHandle, bool unicastNacks) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ClientSetUnicastNacks(unicastNacks); } // end NormSetDefaultUnicastNack() void NormNodeSetUnicastNack(NormNodeHandle nodeHandle, bool unicastNacks) { NormServerNode* node = (NormServerNode*)nodeHandle; if (node) node->SetUnicastNacks(unicastNacks); } // end NormNodeSetUnicastNack() void NormSetDefaultNackingMode(NormSessionHandle sessionHandle, NormNackingMode nackingMode) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ClientSetDefaultNackingMode((NormObject::NackingMode)nackingMode); } // end NormSetDefaultNackingMode() void NormNodeSetNackingMode(NormNodeHandle nodeHandle, NormNackingMode nackingMode) { NormServerNode* node = (NormServerNode*)nodeHandle; if (node) node->SetDefaultNackingMode((NormObject::NackingMode)nackingMode); } // end NormNodeSetNackingMode() void NormObjectSetNackingMode(NormObjectHandle objectHandle, NormNackingMode nackingMode) { NormObject* object = (NormObject*)objectHandle; if (object) object->SetNackingMode((NormObject::NackingMode)nackingMode); } // end NormObjectSetNackingMode() void NormSetDefaultRepairBoundary(NormSessionHandle sessionHandle, NormRepairBoundary repairBoundary) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ClientSetDefaultRepairBoundary((NormServerNode::RepairBoundary)repairBoundary); } // end NormSetDefaultRepairBoundary() void NormNodeSetRepairBoundary(NormNodeHandle nodeHandle, NormRepairBoundary repairBoundary) { NormServerNode* node = static_cast((NormNode*)nodeHandle); if (node) node->SetRepairBoundary((NormServerNode::RepairBoundary)repairBoundary); } // end NormNodeSetRepairBoundary() bool NormStreamRead(NormObjectHandle streamHandle, char* buffer, unsigned int* numBytes) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if (instance && instance->dispatcher.SuspendThread()) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (stream) result = stream->Read(buffer, numBytes); instance->dispatcher.ResumeThread(); } return result; } // end NormStreamRead() bool NormStreamSeekMsgStart(NormObjectHandle streamHandle) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if (instance && instance->dispatcher.SuspendThread()) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); unsigned int numBytes = 0; if (stream) result = stream->Read(NULL, &numBytes, true); instance->dispatcher.ResumeThread(); } return result; } // end NormStreamSeekMsgStart() unsigned long NormStreamGetReadOffset(NormObjectHandle streamHandle) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); return stream->GetCurrentReadOffset(); } // end NormStreamGetReadOffset() /** NORM Object Functions */ NormObjectType NormObjectGetType(NormObjectHandle objectHandle) { if (NORM_OBJECT_INVALID != objectHandle) return ((NormObjectType)(((NormObject*)objectHandle)->GetType())); else return NORM_OBJECT_NONE; } // end NormObjectGetType() bool NormObjectHasInfo(NormObjectHandle objectHandle) { return ((NormObject*)objectHandle)->HasInfo(); } // end NormObjectHasInfo() unsigned short NormObjectGetInfoLength(NormObjectHandle objectHandle) { return ((NormObject*)objectHandle)->GetInfoLength(); } // end NormObjectGetInfoLength() unsigned short NormObjectGetInfo(NormObjectHandle objectHandle, char* buffer, unsigned short bufferLen) { unsigned short result = 0; if (NORM_OBJECT_INVALID != objectHandle) { NormObject* object = (NormObject*)objectHandle; if (object->HaveInfo()) { result = object->GetInfoLength(); if (result < bufferLen) bufferLen = result; if (buffer) memcpy(buffer, object->GetInfo(), bufferLen); } } return result; } // end NormObjectGetInfo() off_t NormObjectGetSize(NormObjectHandle objectHandle) { return ((NormObject*)objectHandle)->GetSize().GetOffset(); } // end NormObjectGetSize() void NormObjectCancel(NormObjectHandle objectHandle) { if (NORM_OBJECT_INVALID != objectHandle) { NormInstance* instance = NormInstance::GetInstanceFromObject(objectHandle); if (instance && instance->dispatcher.SuspendThread()) { NormObject* obj = (NormObject*)objectHandle; NormServerNode* server = obj->GetServer(); if (server) server->DeleteObject(obj); else obj->GetSession().DeleteTxObject(obj); instance->PurgeObjectNotifications(objectHandle); instance->dispatcher.ResumeThread(); } } } // end NormObjectCancel() void NormObjectRetain(NormObjectHandle objectHandle) { if (NORM_OBJECT_INVALID != objectHandle) { NormInstance* instance = NormInstance::GetInstanceFromObject(objectHandle); if (instance && instance->dispatcher.SuspendThread()) { NormObject* obj = (NormObject*)objectHandle; obj->Retain(); instance->dispatcher.ResumeThread(); } } } // end NormRetainObject() void NormObjectRelease(NormObjectHandle objectHandle) { if (NORM_OBJECT_INVALID != objectHandle) { NormInstance* instance = NormInstance::GetInstanceFromObject(objectHandle); if (instance && instance->dispatcher.SuspendThread()) { NormObject* obj = (NormObject*)objectHandle; obj->Release(); instance->PurgeObjectNotifications(objectHandle); instance->dispatcher.ResumeThread(); } } } // end NormObjectRelease() bool NormFileGetName(NormObjectHandle fileHandle, char* nameBuffer, unsigned int bufferLen) { bool result = false; if (NORM_OBJECT_INVALID != fileHandle) { // (TBD) verify "fileHandle" is a NORM_FILE ?>?? NormFileObject* file = static_cast((NormObject*)fileHandle); bufferLen = (bufferLen < PATH_MAX) ? bufferLen : PATH_MAX; strncpy(nameBuffer, file->GetPath(), bufferLen); nameBuffer[bufferLen=1] = '\0'; result = true; } return result; } // end NormFileGetName() bool NormFileRename(NormObjectHandle fileHandle, const char* fileName) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromObject(fileHandle); if (instance && instance->dispatcher.SuspendThread()) { // (TBD) verify "fileHandle" is a NORM_FILE ?>?? NormFileObject* file = static_cast((NormObject*)fileHandle); result = file->Rename(fileName); instance->dispatcher.ResumeThread(); } return result; } // end NormFileRename() const char*volatile NormDataAccessData(NormObjectHandle dataHandle) { NormDataObject* dataObj = static_cast((NormObject*)dataHandle); return dataObj->GetData(); } // end NormDataAccessData() char* NormDataDetachData(NormObjectHandle dataHandle) { NormDataObject* dataObj = static_cast((NormObject*)dataHandle); return dataObj->DetachData(); } // end NormDataDetachData() NormNodeHandle NormObjectGetSender(NormObjectHandle objectHandle) { return (NormNodeHandle)(((NormObject*)objectHandle)->GetServer()); } // end NormObjectGetSender() /** NORM Node Functions */ NormNodeId NormNodeGetId(NormNodeHandle nodeHandle) { NormNode* node = (NormNode*)nodeHandle; if (node) return node->GetId(); else return NORM_NODE_NONE; } // end NormNodeGetId() bool NormNodeGetAddress(NormNodeHandle nodeHandle, char* addrBuffer, unsigned int* bufferLen, unsigned short* port) { bool result = false; if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { NormNode* node = (NormNode*)nodeHandle; const ProtoAddress& nodeAddr = node->GetAddress(); unsigned int addrLen = nodeAddr.GetLength(); if (addrBuffer && bufferLen && (addrLen <= *bufferLen)) { memcpy(addrBuffer, nodeAddr.GetRawHostAddress(), addrLen); result = true; } else if (NULL == addrBuffer) { result = true; // just a query for addrLen and/or port } if (bufferLen) *bufferLen = addrLen; if (port) *port = nodeAddr.GetPort(); instance->dispatcher.ResumeThread(); } } return result; } // end NormNodeGetId() void NormNodeRetain(NormNodeHandle nodeHandle) { if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { NormNode* node = (NormNode*)nodeHandle; node->Retain(); instance->dispatcher.ResumeThread(); } } } // end NormNodeRetain() void NormNodeRelease(NormNodeHandle nodeHandle) { if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { NormNode* node = (NormNode*)nodeHandle; node->Release(); instance->dispatcher.ResumeThread(); } } } // end NormNodeRelease() // (TBD) Some stream i/o performance improvement can be realized // if the option to make "NormWriteStream()" and // "NormReadStream()" _blocking_ calls is implemented. // Right now, the stream read/write are non-blocking // so applications should use "NormGetNextEvent()" to // know when to read or write ... This results in the // underlying NORM thread to be suspended/resumed _twice_ // per read or write ... It would be more efficient to // "suspend" the NORM thread while the application // processes all pending events and _then_ "resume" the // NORM thread ... an approach to this would be enable // the app to install an event handler callback and dispatch // events with a "NormDispatchEvents()" call ... //