#define _NORM_API_BUILD // force 'dllexport' in "normApi.h" #include "normApi.h" #include "normSession.h" #ifdef WIN32 #ifndef _WIN32_WCE #include // for _mktemp() #endif // !_WIN32_WCE #endif // WIN32 // const defs extern NORM_API_LINKAGE const NormInstanceHandle NORM_INSTANCE_INVALID = ((NormInstanceHandle)0); extern NORM_API_LINKAGE const NormSessionHandle NORM_SESSION_INVALID = ((NormSessionHandle)0); extern NORM_API_LINKAGE const NormNodeHandle NORM_NODE_INVALID = ((NormNodeHandle)0); extern NORM_API_LINKAGE const NormNodeId NORM_NODE_NONE = ((NormNodeId)0x00000000); extern NORM_API_LINKAGE const NormNodeId NORM_NODE_ANY = ((NormNodeId)0xffffffff); extern NORM_API_LINKAGE const NormObjectHandle NORM_OBJECT_INVALID = ((NormObjectHandle)0); extern NORM_API_LINKAGE 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 NormNode* node, class NormObject* object); bool Startup(bool priorityBoost = false); void Shutdown(); void Stop() // pause NORM protocol engine { dispatcher.Stop(); Notify(NormController::EVENT_INVALID, &session_mgr, NULL, NULL, NULL); } bool Start() { if (dispatcher.StartThread(priority_boost)) { return true; } else { PLOG(PL_FATAL, "NormInstance::Resume() error restarting NORM thread\n"); return false; } } bool WaitForEvent(); bool GetNextEvent(NormEvent* theEvent); bool SetCacheDirectory(const char* cachePath); void SetAllocationFunctions(NormAllocFunctionHandle allocFunc, NormFreeFunctionHandle freeFunc) { data_alloc_func = allocFunc; session_mgr.SetDataFreeFunction(freeFunc); } void ReleasePreviousEvent(); bool NotifyQueueIsEmpty() const {return notify_queue.IsEmpty();} void PurgeSessionNotifications(NormSessionHandle sessionHandle); void PurgeNodeNotifications(NormNodeHandle nodeHandle); void PurgeObjectNotifications(NormObjectHandle objectHandle); void PurgeNotifications(NormSessionHandle sessionHandle, NormEventType eventType); UINT32 CountCompletedObjects(NormSession* theSession); ProtoDispatcher::Descriptor GetDescriptor() const { #ifdef WIN32 return notify_event; #else return notify_fd[0]; #endif // if/else WIN32/UNIX } static NormInstance* GetInstanceFromSession(NormSessionHandle sessionHandle) { if (NORM_SESSION_INVALID == sessionHandle) return ((NormInstance*)NULL); NormSession* session = (NormSession*)sessionHandle; NormInstance* theInstance = static_cast(session->GetSessionMgr().GetController()); return theInstance; } static NormInstance* GetInstanceFromNode(NormNodeHandle nodeHandle) { if (NORM_NODE_INVALID == nodeHandle) return ((NormInstance*)NULL); NormSession& session = ((NormNode*)nodeHandle)->GetSession(); return static_cast(session.GetSessionMgr().GetController()); } static NormInstance* GetInstanceFromObject(NormObjectHandle objectHandle) { if (NORM_OBJECT_INVALID == objectHandle) return ((NormInstance*)NULL); NormSession& session = ((NormObject*)objectHandle)->GetSession();; return static_cast(session.GetSessionMgr().GetController()); } class Notification : public ProtoList::Item { public: NormEvent event; class Queue : public ProtoListTemplate {}; }; // end class NormInstance::Notification ProtoDispatcher dispatcher; bool priority_boost; NormSessionMgr session_mgr; NormAllocFunctionHandle data_alloc_func; private: void ResetNotificationEvent() { #ifdef WIN32 if (0 == ResetEvent(notify_event)) PLOG(PL_ERROR, "NormInstance::ResetNotificationEvent() 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 } 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]; // TBD - use eventfd on Linux for this, may EVT_USER on MacOS #endif // if/else WIN32/UNIX }; // end class NormInstance //////////////////////////////////////////////////// // NormInstance implementation NormInstance::NormInstance() : priority_boost(false), session_mgr(static_cast(dispatcher), static_cast(dispatcher), static_cast(&dispatcher)), data_alloc_func(NULL), 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 dispatcher.SetUserData(&session_mgr); // for debugging session_mgr.SetController(static_cast(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()) { size_t 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 { PLOG(PL_ERROR, "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 NormNode* node, class NormObject* object) { switch (event) { case SEND_OK: // Purge any pending NORM_SEND_ERROR notifications for session PurgeNotifications(session, NORM_SEND_ERROR); return; default: break; } // (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* next = notify_pool.RemoveHead(); if (NULL == next) { if (NULL == (next = new Notification)) { PLOG(PL_FATAL, "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_receiver 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)) { PLOG(PL_FATAL, "NormInstance::Notify() stream accept error\n"); notify_pool.Append(*next); 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]; #ifdef WIN32 #ifdef _WIN32_WCE bool tempFileOK = false; for (int i = 0; i < 255; i++) { snprintf(fileName, PATH_MAX, "%snormTemp%06u", rx_cache_path, i); if(!NormFile::IsLocked(fileName)) { tempFileOK = true; break; } } if (!tempFileOK) #else snprintf(fileName, PATH_MAX, "%snormTempXXXXXX", rx_cache_path); if (!_mktemp(fileName)) #endif // if/else _WIN32_WCE #else snprintf(fileName, PATH_MAX, "%snormTempXXXXXX", rx_cache_path); int fd = mkstemp(fileName); if (fd >= 0) { close(fd); } else #endif // if/else WIN32 { PLOG(PL_ERROR, "NormInstance::Notify(RX_OBJECT_NEW) Warning: mkstemp() error: %s\n", GetErrorString()); } if (!static_cast(object)->Accept(fileName)) { PLOG(PL_ERROR, "NormInstance::Notify(RX_OBJECT_NEW) file object accept error!\n"); } } else { // we're ignoring files PLOG(PL_DETAIL, "NormInstance::Notify() warning: receive file but no cache directory set, so ignoring file\n"); notify_pool.Append(*next); return; } break; } case NormObject::DATA: { NormDataObject* dataObj = static_cast(object); unsigned int dataLen = (unsigned int)(object->GetSize().GetOffset()); char* dataPtr = (NULL != data_alloc_func) ? data_alloc_func(dataLen) : new char[dataLen]; if (NULL == dataPtr) { PLOG(PL_FATAL, "NormInstance::Notify(RX_OBJECT_NEW) new dataPtr error: %s\n", GetErrorString()); notify_pool.Append(*next); 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) if (!dataObj->Accept(dataPtr, dataLen, true)) { PLOG(PL_FATAL, "NormInstance::Notify() data object accept error\n"); notify_pool.Append(*next); return; } break; } default: // This shouldn't occur notify_pool.Append(*next); return; } // end switch(object->GetType()) break; } // end case RX_OBJECT_NEW default: break; } // end switch(event) // "Retain" any valid "object" or "sender" handles for API access if (NORM_OBJECT_INVALID != object) ((NormObject*)object)->Retain(); else if (NORM_NODE_INVALID != node) ((NormNode*)node)->Retain(); bool doNotify = notify_queue.IsEmpty(); next->event.type = (NormEventType)event; next->event.session = session; next->event.sender = node; next->event.object = object; notify_queue.Append(*next); if (doNotify) { #ifdef WIN32 if (0 == SetEvent(notify_event)) { PLOG(PL_ERROR, "NormInstance::Notify() SetEvent() error: %s\n", GetErrorString()); } #else char byte = 0; while (1 != write(notify_fd[1], &byte, 1)) { if ((EINTR != errno) && (EAGAIN != errno)) { PLOG(PL_FATAL, "NormInstance::Notify() write() error: %s\n", GetErrorString()); break; } } #endif // if/else WIN32/UNIX } } // end NormInstance::Notify() // Purge any notifications associated with a specific object void NormInstance::PurgeObjectNotifications(NormObjectHandle objectHandle) { if (NORM_OBJECT_INVALID == objectHandle) return; Notification::Queue::Iterator iterator(notify_queue); Notification* next; while (NULL != (next = iterator.GetNextItem())) { if (objectHandle == next->event.object) { // "Release" the previously-retained object handle ((NormObject*)objectHandle)->Release(); // Remove from queue and put in pool notify_queue.Remove(*next); notify_pool.Append(*next); } } if ((NULL != previous_notification) && (objectHandle == previous_notification->event.object)) { // "Release" any previously-retained object or node handle ((NormObject*)(previous_notification->event.object))->Release(); notify_pool.Append(*previous_notification); previous_notification = NULL; } // TBD - check if event queue is emptied and reset event/fd } // end NormInstance::PurgeObjectNotifications() // Purge any notifications associated with a specific remote sender node void NormInstance::PurgeNodeNotifications(NormNodeHandle nodeHandle) { if (NORM_NODE_INVALID == nodeHandle) return; Notification::Queue::Iterator iterator(notify_queue); Notification* next; while (NULL != (next = iterator.GetNextItem())) { if (nodeHandle == next->event.sender) { // "Release" the previously-retained object handle ((NormNode*)nodeHandle)->Release(); // Remove this notification from queue and return to pool notify_queue.Remove(*next); notify_pool.Append(*next); } } if ((NULL != previous_notification) && (nodeHandle == previous_notification->event.sender)) { // "Release" any previously-retained object or node handle if (NORM_OBJECT_INVALID != previous_notification->event.object) ((NormObject*)(previous_notification->event.object))->Release(); else ((NormNode*)(previous_notification->event.sender))->Release(); notify_pool.Append(*previous_notification); previous_notification = NULL; } if (notify_queue.IsEmpty()) ResetNotificationEvent(); } // end NormInstance::PurgeNodeNotifications() void NormInstance::PurgeSessionNotifications(NormSessionHandle sessionHandle) { if (NORM_SESSION_INVALID == sessionHandle) return; Notification::Queue::Iterator iterator(notify_queue); Notification* next; while (NULL != (next = iterator.GetNextItem())) { if (next->event.session == sessionHandle) { if (NORM_OBJECT_INVALID != next->event.object) ((NormObject*)next->event.object)->Release(); else if (NORM_NODE_INVALID != next->event.sender) ((NormNode*)next->event.sender)->Release(); // Remove this notification from queue and return to pool notify_queue.Remove(*next); notify_pool.Append(*next); } } if ((NULL != previous_notification) && (sessionHandle == previous_notification->event.session)) { // "Release" any previously-retained object or node handle if (NORM_OBJECT_INVALID != previous_notification->event.object) ((NormObject*)(previous_notification->event.object))->Release(); else if (NORM_NODE_INVALID != previous_notification->event.sender) ((NormNode*)(previous_notification->event.sender))->Release(); notify_pool.Append(*previous_notification); previous_notification = NULL; } if (notify_queue.IsEmpty()) ResetNotificationEvent(); } // end NormInstance::PurgeSessionNotifications() // Purges notifications of a specific type for a specific session void NormInstance::PurgeNotifications(NormSessionHandle sessionHandle, NormEventType eventType) { if (NORM_SESSION_INVALID == sessionHandle) return; Notification::Queue::Iterator iterator(notify_queue); Notification* next; while (NULL != (next = iterator.GetNextItem())) { if ((next->event.session == sessionHandle) && (next->event.type == eventType)) { if (NORM_OBJECT_INVALID != next->event.object) ((NormObject*)next->event.object)->Release(); else if (NORM_NODE_INVALID != next->event.sender) ((NormNode*)next->event.sender)->Release(); // Remove this notification from queue and return to pool notify_queue.Remove(*next); notify_pool.Append(*next); } } if (notify_queue.IsEmpty()) ResetNotificationEvent(); } // end NormInstance::PurgeNotifications() // 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 previously-retained object or node handle if (NORM_OBJECT_INVALID != previous_notification->event.object) ((NormObject*)(previous_notification->event.object))->Release(); else if (NORM_NODE_INVALID != previous_notification->event.sender) ((NormNode*)(previous_notification->event.sender))->Release(); notify_pool.Append(*previous_notification); previous_notification = NULL; } Notification* next; while (NULL != (next = notify_queue.RemoveHead())) { switch (next->event.type) { case NORM_EVENT_INVALID: if (!notify_queue.IsEmpty()) { // Discard this invalid event and get next one notify_pool.Append(*next); continue; } break; case NORM_RX_OBJECT_UPDATED: { // reset update event notification for non-streams // (NormStreamRead() takes care of streams) NormObject* obj = ((NormObject*)next->event.object); if (!obj->IsStream()) obj->SetNotifyOnUpdate(true); break; } case NORM_SEND_ERROR: { NormSession* session = (NormSession*)next->event.session; session->ClearSendError(); break; } default: break; } break; } if (NULL != next) { previous_notification = next; // keep dispatched event for garbage collection if (NULL != theEvent) *theEvent = next->event; } else if (NULL != theEvent) { theEvent->type = NORM_EVENT_INVALID; theEvent->session = NORM_SESSION_INVALID; theEvent->sender = NORM_NODE_INVALID; theEvent->object = NORM_OBJECT_INVALID; } if (notify_queue.IsEmpty()) ResetNotificationEvent(); return (NULL != next); } // end NormInstance::GetNextEvent() bool NormInstance::WaitForEvent() { if (!dispatcher.IsThreaded()) { PLOG(PL_FATAL, "NormInstance::WaitForEvent() warning: NORM thread not running!\n"); return false; } #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) { PLOG(PL_FATAL, "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) { PLOG(PL_FATAL, "NormInstance::Startup() CreateEvent() error: %s\n", GetErrorString()); return false; } #else if (0 != pipe(notify_fd)) { PLOG(PL_FATAL, "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)) { PLOG(PL_FATAL, "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 priority_boost = priorityBoost; return dispatcher.StartThread(priorityBoost); } // end NormInstance::Startup() void NormInstance::ReleasePreviousEvent() { // Garbage collect our "previous_notification" if (NULL != previous_notification) { // Release any previously-retained object or node handles if (NORM_OBJECT_INVALID != previous_notification->event.object) ((NormObject*)(previous_notification->event.object))->Release(); else if (NORM_NODE_INVALID != previous_notification->event.sender) ((NormNode*)(previous_notification->event.sender))->Release(); notify_pool.Append(*previous_notification); previous_notification = NULL; } } // end NormInstance::ReleasePreviousEvent() NORM_API_LINKAGE void NormReleasePreviousEvent(NormInstanceHandle instanceHandle) { NormInstance* instance = (NormInstance*)instanceHandle; if (instance && instance->dispatcher.SuspendThread()) { instance->ReleasePreviousEvent(); instance->dispatcher.ResumeThread(); } } // end NormReleasePreviousEvent() 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) { // Release any previously-retained object or node handles if (NORM_OBJECT_INVALID != previous_notification->event.object) ((NormObject*)(previous_notification->event.object))->Release(); else if (NORM_NODE_INVALID != previous_notification->event.sender) ((NormNode*)(previous_notification->event.sender))->Release(); notify_pool.Append(*previous_notification); previous_notification = NULL; } Notification* next; while (NULL != (next = notify_queue.RemoveHead())) { switch (next->event.type) { case NORM_RX_OBJECT_NEW: { NormObject* obj = (NormObject*)next->event.object; switch (obj->GetType()) { case NormObject::FILE: // (TBD) unlink temp file? break; default: break; } break; } default: break; } if (NORM_OBJECT_INVALID != next->event.object) ((NormObject*)(next->event.object))->Release(); else if (NORM_NODE_INVALID != next->event.sender) ((NormNode*)(next->event.sender))->Release(); delete next; } notify_pool.Destroy(); } // end NormInstance::Shutdown() // This function doesn't make sense? UINT32 NormInstance::CountCompletedObjects(NormSession* session) { UINT32 result = 0UL; Notification::Queue::Iterator iterator(notify_queue); Notification* next; while (NULL != (next = iterator.GetNextItem())) { if ((session == next->event.session) && (NORM_RX_OBJECT_COMPLETED == next->event.type)) { result ++; } } return result; } // end NormInstance::CountCompletedObjects() ////////////////////////////////////////////////////////////////////////// // NORM API FUNCTION IMPLEMENTATIONS // NORM_API_LINKAGE int NormGetVersion(int* major, int* minor, int* patch) { // TBD - use normVersion.h info instead if (NULL != major) *major = NORM_VERSION_MAJOR; if (NULL != minor) *minor = NORM_VERSION_MINOR; if (NULL != patch) *patch = NORM_VERSION_PATCH; return NORM_VERSION_MAJOR; } // end NormGetVersion() /** NORM API Initialization */ NORM_API_LINKAGE 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() NORM_API_LINKAGE void NormDestroyInstance(NormInstanceHandle instanceHandle) { delete ((NormInstance*)instanceHandle); } // end NormDestroyInstance() NORM_API_LINKAGE void NormStopInstance(NormInstanceHandle instanceHandle) { NormInstance* instance = (NormInstance*)instanceHandle; if (instance) instance->Stop(); // stops NORM protocol thread } // end NormStopInstance() NORM_API_LINKAGE bool NormRestartInstance(NormInstanceHandle instanceHandle) { NormInstance* instance = (NormInstance*)instanceHandle; if (instance) { if (instance->dispatcher.IsThreaded()) instance->Stop(); return instance->Start(); } return false; // invalid instanceHandle } // end NormStartInstance() NORM_API_LINKAGE bool NormSuspendInstance(NormInstanceHandle instanceHandle) { NormInstance* instance = (NormInstance*)instanceHandle; if (instance) return instance->dispatcher.SuspendThread(); // stops NORM protocol thread else return false; } // end NormSuspendInstance() NORM_API_LINKAGE void NormResumeInstance(NormInstanceHandle instanceHandle) { NormInstance* instance = (NormInstance*)instanceHandle; if (instance) instance->dispatcher.ResumeThread(); } // end NormResumeInstance() NORM_API_LINKAGE bool NormSetCacheDirectory(NormInstanceHandle instanceHandle, const char* cachePath) { NormInstance* instance = (NormInstance*)instanceHandle; if (instance) return instance->SetCacheDirectory(cachePath); else return false; } // end NormSetCacheDirectory() NORM_API_LINKAGE NormDescriptor NormGetDescriptor(NormInstanceHandle instanceHandle) { NormInstance* instance = (NormInstance*)instanceHandle; if (instance) return (instance->GetDescriptor()); else return NORM_DESCRIPTOR_INVALID; } // end NormGetDescriptor() NORM_API_LINKAGE void NormSetAllocationFunctions(NormInstanceHandle instanceHandle, NormAllocFunctionHandle allocFunc, NormFreeFunctionHandle freeFunc) { NormInstance* instance = (NormInstance*)instanceHandle; instance->SetAllocationFunctions(allocFunc, freeFunc); } // end NormSetAllocationFunctions() // if "waitForEvent" is false, this is a non-blocking call // (TBD) add a timeout option to this? NORM_API_LINKAGE bool NormGetNextEvent(NormInstanceHandle instanceHandle, NormEvent* theEvent, bool waitForEvent) { NormInstance* instance = (NormInstance*)instanceHandle; bool result = false; if (instance) { if (instance->dispatcher.SuspendThread()) { if (waitForEvent) { if (instance->NotifyQueueIsEmpty()) { // no pending events, so resume and wait instance->dispatcher.ResumeThread(); if (!instance->WaitForEvent()) { // Indication that NormInstance is dead // TBD - how do we inform app although this shouldn't // happen unless the app destroys the "instance" return false; } // re-suspend thread after wait if (!instance->dispatcher.SuspendThread()) return false; } } result = instance->GetNextEvent(theEvent); instance->dispatcher.ResumeThread(); } } return result; } // end NormGetNextEvent() NORM_API_LINKAGE bool NormIsUnicastAddress(const char* address) { // TBD - is this really a thread-safe thing to do? ProtoAddress addr; if ((NULL != address) && addr.ResolveFromString(address)) return addr.IsUnicast(); else return false; // not valid, so not unicast } // end NormIsUnicast() /** NORM Session Creation and Control Functions */ NORM_API_LINKAGE NormSessionHandle NormCreateSession(NormInstanceHandle instanceHandle, const char* sessionAddr, UINT16 sessionPort, NormNodeId localNodeId) { // (TBD) wrap this with SuspendThread/ResumeThread ??? NormInstance* instance = (NormInstance*)instanceHandle; if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = instance->session_mgr.NewSession(sessionAddr, sessionPort, localNodeId); instance->dispatcher.ResumeThread(); if (NULL != session) return ((NormSessionHandle)session); } return NORM_SESSION_INVALID; } // end NormCreateSession() NORM_API_LINKAGE void NormDestroySession(NormSessionHandle sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (NULL != session) { session->Close(); session->GetSessionMgr().DeleteSession(session); instance->PurgeSessionNotifications(sessionHandle); } instance->dispatcher.ResumeThread(); } } // end NormDestroySession() NORM_API_LINKAGE NormInstanceHandle NormGetInstance(NormSessionHandle sessionHandle) { return (NormInstanceHandle)NormInstance::GetInstanceFromSession(sessionHandle); } // end NormGetIntance() NORM_API_LINKAGE void NormSetUserData(NormSessionHandle sessionHandle, const void* userData) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetUserData(userData); instance->dispatcher.ResumeThread(); } } // end NormSetUserData() NORM_API_LINKAGE 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() NORM_API_LINKAGE void NormSetUserTimer(NormSessionHandle sessionHandle, double seconds) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetUserTimer(seconds); instance->PurgeNotifications(sessionHandle, NORM_USER_TIMEOUT); instance->dispatcher.ResumeThread(); } } } // end NormSetUserTimer() NORM_API_LINKAGE void NormCancelUserTimer(NormSessionHandle sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetUserTimer(-1.0); // interval less than zero cancels timer instance->PurgeNotifications(sessionHandle, NORM_USER_TIMEOUT); instance->dispatcher.ResumeThread(); } } } // end NormCancelUserTimer() NORM_API_LINKAGE NormNodeId NormGetLocalNodeId(NormSessionHandle sessionHandle) { NormSession* session = (NormSession*)sessionHandle; if (NULL != session) return session->LocalNodeId(); else return NORM_NODE_NONE; } // end NormGetLocalNodeId() NORM_API_LINKAGE bool NormGetAddress(NormSessionHandle sessionHandle, char* addrBuffer, unsigned int* bufferLen, UINT16* port) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; const ProtoAddress& sessionAddr = session->Address(); unsigned int addrLen = sessionAddr.GetLength(); if (addrBuffer && bufferLen && (addrLen <= *bufferLen)) { memcpy(addrBuffer, sessionAddr.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 = sessionAddr.GetPort(); instance->dispatcher.ResumeThread(); } } return result; } // end NormGetAddress() NORM_API_LINKAGE UINT16 NormGetRxPort(NormSessionHandle sessionHandle) { NormSession* session = (NormSession*)sessionHandle; if (NULL != session) return session->GetRxPort(); else return 0; } // end NormGetRxPort() NORM_API_LINKAGE bool NormGetRxBindAddress(NormSessionHandle sessionHandle, char* addr, unsigned int& addrLen, UINT16& port) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if ((NULL != instance) && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; port = session->GetRxPort(); ProtoAddress bindAddr = session->GetRxBindAddr(); if (!bindAddr.IsValid()) { addrLen = 0; result = true; } else if (addrLen < bindAddr.GetLength()) { addrLen = bindAddr.GetLength(); } else { addrLen = bindAddr.GetLength(); memcpy(addr, bindAddr.GetRawHostAddress(), addrLen); result = true; } instance->dispatcher.ResumeThread(); } else { addrLen = port = 0; } return result; } // end NormGetRxBindAddress() NORM_API_LINKAGE bool NormSetTxPort(NormSessionHandle sessionHandle, UINT16 txPort, bool enableReuse, const char* txAddress) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (NULL != session) result = session->SetTxPort(txPort, enableReuse, txAddress); instance->dispatcher.ResumeThread(); } } return result; } // end NormSetTxPort() NORM_API_LINKAGE UINT16 NormGetTxPort(NormSessionHandle sessionHandle) { NormSession* session = (NormSession*)sessionHandle; if (NULL != session) return session->GetTxPort(); else return 0; } // end NormGetTxPort() NORM_API_LINKAGE void NormSetTxOnly(NormSessionHandle sessionHandle, bool txOnly, bool connectToSessionAddress) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetTxOnly(txOnly, connectToSessionAddress); instance->dispatcher.ResumeThread(); } } // end NormSetTxOnly() NORM_API_LINKAGE bool NormPresetObjectInfo(NormSessionHandle sessionHandle, unsigned long objectSize, UINT16 segmentSize, UINT16 numData, UINT16 numParity) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) { result = session->SetPresetFtiData((unsigned int)objectSize, segmentSize, numData, numParity); if (result) session->SenderSetFtiMode(NormSession::FTI_PRESET); } instance->dispatcher.ResumeThread(); } return result; } // end NormPresetObjectInfo() NORM_API_LINKAGE void NormLimitObjectInfo(NormSessionHandle sessionHandle, bool state) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) { if (state) session->SenderSetFtiMode(NormSession::FTI_INFO); else session->SenderSetFtiMode(NormSession::FTI_ALWAYS); } instance->dispatcher.ResumeThread(); } } // end NormLimitObjectInfo() // These functions are used internally by the NormSocket API extension NORM_API_LINKAGE void NormSetId(NormSessionHandle sessionHandle, NormNodeId normId) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (NULL != session) session->SetNodeId(normId); instance->dispatcher.ResumeThread(); } } // end NormSetId() NORM_API_LINKAGE bool NormChangeDestination(NormSessionHandle sessionHandle, const char* sessionAddress, UINT16 sessionPort, bool connectToSessionAddress) { NormSession* session = (NormSession*)sessionHandle; if (NULL == session) return false; // First, see if we can make a valid ProtoAddress ProtoAddress dest; if (NULL != sessionAddress) { if (!dest.ResolveFromString(sessionAddress)) return false; } else { dest = session->Address(); } dest.SetPort(sessionPort); NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { if (NULL != session) { session->SetAddress(dest); if (connectToSessionAddress) session->SetTxOnly(session->GetTxOnly(), true); } instance->dispatcher.ResumeThread(); } return true; } // end NormChangeDestination() NORM_API_LINKAGE void NormSetServerListener(NormSessionHandle sessionHandle, bool state) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetServerListener(state); instance->dispatcher.ResumeThread(); } } } // end NormSetServerListener() NORM_API_LINKAGE bool NormTransferSender(NormSessionHandle sessionHandle, NormNodeHandle senderHandle) { // The NormSession::InsertRemoteSender() used below synthesizes a NORM_CMD(CC) that // is injected into the "sessionHandle" NormSession which results in a new remote // sender being established. I.e., it basically "clones" the sender from another // NormSession into the target NormSession bool result = false; NormInstance* dstInstance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != dstInstance) { if (dstInstance->dispatcher.SuspendThread()) { NormInstance* srcInstance = NormInstance::GetInstanceFromNode(senderHandle); if (srcInstance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; NormSenderNode* sender = (NormSenderNode*)senderHandle; if ((NULL != session) && (NULL != sender)) result = session->InsertRemoteSender(*sender); srcInstance->dispatcher.ResumeThread(); } dstInstance->dispatcher.ResumeThread(); } } return result; } // end NormTransferSender() NORM_API_LINKAGE void NormSetRxPortReuse(NormSessionHandle sessionHandle, bool enableReuse, const char* rxAddress, // bind() to / const char* senderAddress, // connect() to / UINT16 senderPort) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetRxPortReuse(enableReuse, rxAddress, senderAddress, senderPort); instance->dispatcher.ResumeThread(); } } } // end NormSetRxPortReuse() NORM_API_LINKAGE void NormSetEcnSupport(NormSessionHandle sessionHandle, bool ecnEnable, bool ignoreLoss, bool tolerateLoss) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetEcnSupport(ecnEnable, ignoreLoss, tolerateLoss); instance->dispatcher.ResumeThread(); } } } // end NormSetEcnSupport() NORM_API_LINKAGE 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() NORM_API_LINKAGE bool NormSetSSM(NormSessionHandle sessionHandle, const char* sourceAddress) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (NULL != instance) { if (instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->SetSSM(sourceAddress); instance->dispatcher.ResumeThread(); } } return result; } // end NormSetSSM() NORM_API_LINKAGE 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() NORM_API_LINKAGE 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->SetTOS(tos); instance->dispatcher.ResumeThread(); } } return result; } // end NormSetTOS() /** Special test/debug support functions */ NORM_API_LINKAGE bool NormSetLoopback(NormSessionHandle sessionHandle, bool state) { NormSession* session = (NormSession*)sessionHandle; if (session) return session->SetLoopback(state); else return false; } // end NormSetLoopback() NORM_API_LINKAGE bool NormSetMulticastLoopback(NormSessionHandle sessionHandle, bool state) { NormSession* session = (NormSession*)sessionHandle; if (session) return session->SetMulticastLoopback(state); else return false; } // end NormSetLoopback() NORM_API_LINKAGE bool NormSetFragmentation(NormSessionHandle sessionHandle, bool state) { NormSession* session = (NormSession*)sessionHandle; if (session) return session->SetFragmentation(state); else return false; } // end NormSetFragmentation() NORM_API_LINKAGE void NormSetMessageTrace(NormSessionHandle sessionHandle, bool state) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetTrace(state); } // end NormSetMessageTrace() NORM_API_LINKAGE void NormSetTxLoss(NormSessionHandle sessionHandle, double percent) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetTxLoss(percent); } // end NormSetTxLoss() NORM_API_LINKAGE void NormSetRxLoss(NormSessionHandle sessionHandle, double percent) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetRxLoss(percent); } // end NormSetRxLoss() NORM_API_LINKAGE bool NormOpenDebugLog(NormInstanceHandle instanceHandle, const char *path) { /* NOTE: This only locks one thread. Multiple NormInstances could cause problems with this. */ bool result = false; NormInstance* instance = (NormInstance*)instanceHandle; if (instance->dispatcher.SuspendThread()) { result = OpenDebugLog(path); instance->dispatcher.ResumeThread(); } return result; } NORM_API_LINKAGE void NormCloseDebugLog(NormInstanceHandle instanceHandle) { /* NOTE: This only locks one thread. Multiple NormInstances could cause problems with this. */ NormInstance* instance = (NormInstance*)instanceHandle; if (instance->dispatcher.SuspendThread()) { CloseDebugLog(); instance->dispatcher.ResumeThread(); } } NORM_API_LINKAGE bool NormOpenDebugPipe(NormInstanceHandle instanceHandle, const char *pipeName) { /* NOTE: This only locks one thread. Multiple NormInstances could cause problems with this. */ bool result = false; NormInstance* instance = (NormInstance*)instanceHandle; if (instance->dispatcher.SuspendThread()) { result = OpenDebugPipe(pipeName); instance->dispatcher.ResumeThread(); } return result; } // end NormOpenDebugPipe() NORM_API_LINKAGE void NormCloseDebugPipe(NormInstanceHandle instanceHandle) { /* NOTE: This only locks one thread. Multiple NormInstances could cause problems with this. */ NormInstance* instance = (NormInstance*)instanceHandle; if (instance->dispatcher.SuspendThread()) { CloseDebugPipe(); instance->dispatcher.ResumeThread(); } } // end NormCloseDebugPipe() NORM_API_LINKAGE void NormSetDebugLevel(unsigned int level) { // Sets underlying Protolib debug leve SetDebugLevel(level); } NORM_API_LINKAGE unsigned int NormGetDebugLevel() { return GetDebugLevel(); } NORM_API_LINKAGE void NormSetReportInterval(NormSessionHandle sessionHandle, double interval) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetReportTimerInterval(interval); instance->dispatcher.ResumeThread(); } } // end NormSetReportInterval() NORM_API_LINKAGE double NormGetReportInterval(NormSessionHandle sessionHandle) { double result = 0; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->GetReportTimerInterval(); instance->dispatcher.ResumeThread(); } return result; } // end NormGetReportInterval() /** NORM Sender Functions */ NORM_API_LINKAGE NormSessionId NormGetRandomSessionId() { ProtoTime currentTime; currentTime.GetCurrentTime(); srand((unsigned int)currentTime.usec()); // seed random number generator return (NormSessionId)rand(); } // end NormGetRandomSessionId() NORM_API_LINKAGE bool NormStartSender(NormSessionHandle sessionHandle, NormSessionId sessionId, UINT32 bufferSpace, UINT16 segmentSize, UINT16 numData, UINT16 numParity, UINT8 fecId) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) result = session->StartSender(sessionId, bufferSpace, segmentSize, numData, numParity, fecId); else result = false; instance->dispatcher.ResumeThread(); } return result; } // end NormStartSender() NORM_API_LINKAGE void NormStopSender(NormSessionHandle sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->StopSender(); instance->dispatcher.ResumeThread(); } } // end NormStopSender() NORM_API_LINKAGE double NormGetTxRate(NormSessionHandle sessionHandle) { if (NORM_SESSION_INVALID != sessionHandle) return (((NormSession*)sessionHandle)->GetTxRate()); else return -1.0; } // end NormGetTxRate() NORM_API_LINKAGE void NormSetTxRate(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 NormSetTxRate() NORM_API_LINKAGE 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() NORM_API_LINKAGE void NormSetFlowControl(NormSessionHandle sessionHandle, double flowControlFactor) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetFlowControl(flowControlFactor); instance->dispatcher.ResumeThread(); } } // end NormSetFlowControl() NORM_API_LINKAGE void NormSetCongestionControl(NormSessionHandle sessionHandle, bool enable, bool adjustRate) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetCongestionControl(enable, adjustRate); instance->dispatcher.ResumeThread(); } } // end NormSetCongestionControl() NORM_API_LINKAGE void NormSetTxRateBounds(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 NormSetTxRateBounds() NORM_API_LINKAGE void NormSetTxCacheBounds(NormSessionHandle sessionHandle, NormSize sizeMax, UINT32 countMin, UINT32 countMax) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; NormObjectSize theSize(sizeMax); if (session) session->SetTxCacheBounds(theSize, countMin, countMax); instance->dispatcher.ResumeThread(); } } // end NormSetTxCacheBounds() NORM_API_LINKAGE void NormSetAutoParity(NormSessionHandle sessionHandle, unsigned char autoParity) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SenderSetAutoParity(autoParity); instance->dispatcher.ResumeThread(); } } // end NormSetAutoParity() NORM_API_LINKAGE bool NormRegisterFecCoder(NormSessionHandle sessionHandle, UINT8 fecId, NormEncoderFactory encoderFactory, NormDecoderFactory decoderFactory, bool isRateless) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) { session->GetSessionMgr().RegisterFecCoder(fecId, encoderFactory, decoderFactory, isRateless); instance->dispatcher.ResumeThread(); return true; } instance->dispatcher.ResumeThread(); } return false; } // end NormRegisterFecCoder() NORM_API_LINKAGE void NormRegisterFecLayout(NormInstanceHandle instanceHandle, UINT8 fecId, const NormFecLayout* layout) { NormInstance* instance = (NormInstance*)instanceHandle; if (instance) instance->SetFecLayout(fecId, layout); } // end NormRegisterFecLayout() NORM_API_LINKAGE void NormSetGrttEstimate(NormSessionHandle sessionHandle, double grttEstimate) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SenderSetGrtt(grttEstimate); instance->dispatcher.ResumeThread(); } } // end NormSetGrttEstimate() NORM_API_LINKAGE double NormGetGrttEstimate(NormSessionHandle sessionHandle) { if (NORM_SESSION_INVALID != sessionHandle) { NormSession* session = (NormSession*)sessionHandle; session->ResetGrttNotification(); return (session->SenderGrtt()); } else { return -1.0; } } // end NormGetGrttEstimate() NORM_API_LINKAGE void NormSetGrttMax(NormSessionHandle sessionHandle, double grttMax) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetGrttMax(grttMax); instance->dispatcher.ResumeThread(); } } // end NormSetGrttMax() NORM_API_LINKAGE void NormSetGrttProbingMode(NormSessionHandle sessionHandle, NormProbingMode probingMode) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; session->SetGrttProbingMode((NormSession::ProbingMode)probingMode); instance->dispatcher.ResumeThread(); } } // end NormSetGrttProbingMode() NORM_API_LINKAGE void NormSetGrttProbingInterval(NormSessionHandle sessionHandle, double intervalMin, double intervalMax) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetGrttProbingInterval(intervalMin, intervalMax); instance->dispatcher.ResumeThread(); } } // end NormSetGrttProbingInterval() NORM_API_LINKAGE void NormSetGrttProbingTOS(NormSessionHandle sessionHandle, UINT8 probeTOS) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; session->SetProbeTOS(probeTOS); instance->dispatcher.ResumeThread(); } } // end NormSetGrttProbingTOS() NORM_API_LINKAGE void NormSetBackoffFactor(NormSessionHandle sessionHandle, double backoffFactor) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (backoffFactor >= 0.0) session->SetBackoffFactor(backoffFactor); instance->dispatcher.ResumeThread(); } } // end NormSetBackoffFactor() NORM_API_LINKAGE void NormSetGroupSize(NormSessionHandle sessionHandle, unsigned int groupSize) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; session->SenderSetGroupSize((double)groupSize); instance->dispatcher.ResumeThread(); } } // end NormSetGroupSize() NORM_API_LINKAGE void NormSetTxRobustFactor(NormSessionHandle sessionHandle, int robustFactor) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; session->SetTxRobustFactor(robustFactor); instance->dispatcher.ResumeThread(); } } // end NormSetTxRobustFactor() NORM_API_LINKAGE 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 = static_cast(session->QueueTxFile(fileName, infoPtr, infoLen)); if (obj) objectHandle = (NormObjectHandle)(obj); } instance->dispatcher.ResumeThread(); } return objectHandle; } // end NormFileEnqueue() NORM_API_LINKAGE NormObjectHandle NormDataEnqueue(NormSessionHandle sessionHandle, const char* dataPtr, UINT32 dataLen, 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 = static_cast(session->QueueTxData(dataPtr, dataLen, infoPtr, infoLen)); if (NULL != obj) objectHandle = (NormObjectHandle)obj; } instance->dispatcher.ResumeThread(); } return objectHandle; } // end NormDataEnqueue() NORM_API_LINKAGE bool NormRequeueObject(NormSessionHandle sessionHandle, NormObjectHandle objectHandle) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) { if (NORM_OBJECT_INVALID != objectHandle) result = session->RequeueTxObject((NormObject*)objectHandle); } instance->dispatcher.ResumeThread(); } return result; } // end NormRequeueObject() NORM_API_LINKAGE NormObjectHandle NormStreamOpen(NormSessionHandle sessionHandle, UINT32 bufferSize, 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) { NormStreamObject* streamObj = session->QueueTxStream(bufferSize, true, infoPtr, infoLen); NormObject* obj = static_cast(streamObj); if (obj) objectHandle = (NormObjectHandle)obj; } instance->dispatcher.ResumeThread(); } return objectHandle; } // end NormStreamOpen() NORM_API_LINKAGE void NormStreamClose(NormObjectHandle streamHandle, bool graceful) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (NULL != stream) { if (graceful && (NULL == stream->GetSender())) { NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if (instance && instance->dispatcher.SuspendThread()) { stream->Close(true); // graceful stream closure instance->dispatcher.ResumeThread(); } } else { NormObjectCancel(streamHandle); } } } // end NormStreamClose() NORM_API_LINKAGE unsigned int NormGetStreamBufferSegmentCount(unsigned int bufferBytes, UINT16 segmentSize, UINT16 blockSize) { // This same computation is performed in NormStreamObject::Open() in "normObject.cpp" // (Note the number of stream buffer segments is always smaller than the // blockSize*pending_mask.GetSize()) unsigned int numBlocks = bufferBytes / (blockSize * segmentSize); if (numBlocks < 2) numBlocks = 2; // NORM enforces a 2-block minimum buffer size return (numBlocks * blockSize); } // end NormGetStreamBufferSegmentCount() NORM_API_LINKAGE unsigned int NormStreamWrite(NormObjectHandle streamHandle, const char* buffer, unsigned int numBytes) { // Note: Since an underlying issue with ProtoDispatcher::SignalThread() had been resolved, // using ProtoDispatcher::SuspendThread() should be sufficient since the underlying // protolib time scheduling, etc. code actually invokes SignalThread() on an // as-needed basis. Thus, using SuspendThread() (lighter weight) should suffice unsigned int result = 0; NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if ((NULL != instance) && instance->dispatcher.SuspendThread()) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); result = stream->Write(buffer, numBytes, false); instance->dispatcher.ResumeThread(); } return result; } // end NormStreamWrite() NORM_API_LINKAGE void NormStreamFlush(NormObjectHandle streamHandle, bool eom, NormFlushMode flushMode) { NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if ((NULL != instance) && instance->dispatcher.SuspendThread()) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); NormStreamObject::FlushMode saveFlushMode = stream->GetFlushMode(); stream->SetFlushMode((NormStreamObject::FlushMode)flushMode); stream->Flush(eom); stream->SetFlushMode(saveFlushMode); instance->dispatcher.ResumeThread(); } } // end NormStreamFlush() NORM_API_LINKAGE void NormStreamSetAutoFlush(NormObjectHandle streamHandle, NormFlushMode flushMode) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (stream) stream->SetFlushMode((NormStreamObject::FlushMode)flushMode); } // end NormStreamSetAutoFlush() NORM_API_LINKAGE void NormStreamSetPushEnable(NormObjectHandle streamHandle, bool state) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (stream) stream->SetPushMode(state); } // end NormStreamSetPushEnable() NORM_API_LINKAGE 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() NORM_API_LINKAGE unsigned int NormStreamGetVacancy(NormObjectHandle streamHandle, unsigned int bytesWanted) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if (instance && instance->dispatcher.SuspendThread()) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (NULL != stream) result = stream->GetVacancy(bytesWanted); instance->dispatcher.ResumeThread(); } return result; } // end NormStreamGetVacancy() NORM_API_LINKAGE 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() NORM_API_LINKAGE bool NormSetWatermark(NormSessionHandle sessionHandle, NormObjectHandle objectHandle, bool overrideFlush) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; NormObject* obj = (NormObject*)objectHandle; if (session && obj) { // Purge any existing NORM_TX_WATERMARK_COMPLETED notifications to be safe instance->PurgeNotifications(sessionHandle, NORM_TX_WATERMARK_COMPLETED); // (segmentId doesn't matter for non-stream) if (obj->IsStream()) { NormStreamObject* stream = static_cast(obj); session->SenderSetWatermark(stream->GetId(), stream->FlushBlockId(), stream->FlushSegmentId(), overrideFlush); } else { NormBlockId blockId = obj->GetFinalBlockId(); NormSegmentId segmentId = obj->GetBlockSize(blockId) - 1; session->SenderSetWatermark(obj->GetId(), blockId, segmentId, overrideFlush); } result = true; } instance->dispatcher.ResumeThread(); } return result; } // end NormSetWatermark() NORM_API_LINKAGE bool NormSetWatermarkEx(NormSessionHandle sessionHandle, NormObjectHandle objectHandle, const char* buffer, unsigned int numBytes, bool overrideFlush) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; NormObject* obj = (NormObject*)objectHandle; if (session && obj) { // Purge any existing NORM_TX_WATERMARK_COMPLETED notifications to be safe instance->PurgeNotifications(sessionHandle, NORM_TX_WATERMARK_COMPLETED); // (segmentId doesn't matter for non-stream) if (obj->IsStream()) { NormStreamObject* stream = static_cast(obj); result = session->SenderSetWatermark(stream->GetId(), stream->FlushBlockId(), stream->FlushSegmentId(), overrideFlush, buffer, numBytes); } else { NormBlockId blockId = obj->GetFinalBlockId(); NormSegmentId segmentId = obj->GetBlockSize(blockId) - 1; result = session->SenderSetWatermark(obj->GetId(), blockId, segmentId, overrideFlush, buffer, numBytes); } } instance->dispatcher.ResumeThread(); } return result; } // end NormSetWatermarkEx() NORM_API_LINKAGE bool NormResetWatermark(NormSessionHandle sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { // Purge any existing NORM_TX_WATERMARK_COMPLETED notifications to be safe instance->PurgeNotifications(sessionHandle, NORM_TX_WATERMARK_COMPLETED); NormSession* session = (NormSession*)sessionHandle; session->SenderResetWatermark(); instance->dispatcher.ResumeThread(); return true; } else { return false; } } // end NormResetWatermark() NORM_API_LINKAGE void NormCancelWatermark(NormSessionHandle sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; session->SenderCancelWatermark(); instance->dispatcher.ResumeThread(); } } // end NormSetWatermark() NORM_API_LINKAGE 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 = (NULL != session->SenderAddAckingNode(nodeId)); instance->dispatcher.ResumeThread(); } return result; } // end NormAddAckingNode() NORM_API_LINKAGE void NormRemoveAckingNode(NormSessionHandle sessionHandle, NormNodeId nodeId) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SenderRemoveAckingNode(nodeId); instance->dispatcher.ResumeThread(); } } // end NormRemoveAckingNode() NORM_API_LINKAGE NormNodeHandle NormGetAckingNodeHandle(NormSessionHandle sessionHandle, NormNodeId nodeId) { if (NORM_SESSION_INVALID != sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; NormAckingNode* acker = session->SenderFindAckingNode(nodeId); instance->dispatcher.ResumeThread(); if (NULL != acker) return ((NormNodeHandle)static_cast(acker)); } } return NORM_NODE_INVALID; } // end NormGetAckingNodeHandle() NORM_API_LINKAGE void NormSetAutoAckingNodes(NormSessionHandle sessionHandle, NormTrackingStatus trackingStatus) { NormSession* session = (NormSession*)sessionHandle; switch (trackingStatus) { case NORM_TRACK_NONE: session->SenderSetAutoAckingNodes(NormSession::TRACK_NONE); break; case NORM_TRACK_RECEIVERS: session->SenderSetAutoAckingNodes(NormSession::TRACK_RECEIVERS); break; case NORM_TRACK_SENDERS: session->SenderSetAutoAckingNodes(NormSession::TRACK_SENDERS); break; case NORM_TRACK_ALL: session->SenderSetAutoAckingNodes(NormSession::TRACK_ALL); break; default: break; } } // end NormSetAutoAckingNodes() NORM_API_LINKAGE NormAckingStatus NormGetAckingStatus(NormSessionHandle sessionHandle, NormNodeId nodeId) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; NormAckingStatus status = (NormAckingStatus)session->SenderGetAckingStatus(nodeId); instance->dispatcher.ResumeThread(); return status; } else { return NORM_ACK_INVALID; } } // end NormGetAckingNodeStatus() NORM_API_LINKAGE bool NormGetNextAckingNode(NormSessionHandle sessionHandle, NormNodeId* nodeId, NormAckingStatus* ackingStatus) { if (NULL == nodeId) return false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; bool result = session->SenderGetNextAckingNode(*nodeId, (NormSession::AckingStatus*)ackingStatus); instance->dispatcher.ResumeThread(); return result; } else { return false; } } // end NormGetNextAckingNode() NORM_API_LINKAGE bool NormGetAckEx(NormSessionHandle sessionHandle, NormNodeId nodeId, char* buffer, unsigned int* buflen) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; bool result = (NormAckingStatus)session->SenderGetAckEx(nodeId, buffer, buflen); instance->dispatcher.ResumeThread(); return result; } if (NULL != buflen) *buflen = 0; return false; } // end NormGetAckEx() NORM_API_LINKAGE bool NormSendCommand(NormSessionHandle sessionHandle, const char* cmdBuffer, unsigned int cmdLength, bool robust) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; bool result = session->SenderSendCmd(cmdBuffer, cmdLength, robust); instance->dispatcher.ResumeThread(); return result; } else { return false; } } // end NormSendCommand() NORM_API_LINKAGE void NormCancelCommand(NormSessionHandle sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; session->SenderCancelCmd(); // we purge in case command was already sent notification posted instance->PurgeNotifications(sessionHandle, NORM_TX_CMD_SENT); instance->dispatcher.ResumeThread(); } } // end NormCancelCommand() // This one is not part of the public API (for NormSocket use currently) bool NormSendCommandTo(NormSessionHandle sessionHandle, const char* cmdBuffer, unsigned int cmdLength, const char* addr, UINT16 port) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; ProtoAddress dest; if (dest.ResolveFromString(addr)) { dest.SetPort(port); result = session->SenderSendAppCmd(cmdBuffer, cmdLength, dest); } instance->dispatcher.ResumeThread(); } return result; } // end NormSendCommandTo() NORM_API_LINKAGE void NormSetSynStatus(NormSessionHandle sessionHandle, bool state) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; session->SenderSetSynStatus(state); instance->dispatcher.ResumeThread(); } } // end NormSetSynStatus() /** NORM Receiver Functions */ NORM_API_LINKAGE bool NormStartReceiver(NormSessionHandle sessionHandle, UINT32 bufferSpace) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; result = session->StartReceiver(bufferSpace); instance->dispatcher.ResumeThread(); } return result; } // end NormStartReceiver() NORM_API_LINKAGE void NormStopReceiver(NormSessionHandle sessionHandle) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; session->StopReceiver(); instance->dispatcher.ResumeThread(); } } // end NormStopReceiver() NORM_API_LINKAGE void NormSetRxCacheLimit(NormSessionHandle sessionHandle, unsigned short countMax) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; if (session) session->SetRxCacheMax(countMax); instance->dispatcher.ResumeThread(); } } // end NormSetRxCacheLimit() NORM_API_LINKAGE 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() NORM_API_LINKAGE void NormSetSilentReceiver(NormSessionHandle sessionHandle, bool silent, INT32 maxDelay) { NormSession* session = (NormSession*)sessionHandle; if (session) { session->ReceiverSetSilent(silent); session->RcvrSetMaxDelay(maxDelay); } } // end NormSetSilentReceiver() NORM_API_LINKAGE void NormSetDefaultUnicastNack(NormSessionHandle sessionHandle, bool unicastNacks) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ReceiverSetUnicastNacks(unicastNacks); } // end NormSetDefaultUnicastNack() NORM_API_LINKAGE void NormNodeSetUnicastNack(NormNodeHandle nodeHandle, bool unicastNacks) { NormNode* node = (NormNode*)nodeHandle; if ((NULL != node) && (NormNode::SENDER == node->GetType())) { NormSenderNode* sender = static_cast(node); sender->SetUnicastNacks(unicastNacks); } } // end NormNodeSetUnicastNack() NORM_API_LINKAGE void NormSetDefaultSyncPolicy(NormSessionHandle sessionHandle, NormSyncPolicy syncPolicy) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ReceiverSetDefaultSyncPolicy((NormSenderNode::SyncPolicy)syncPolicy); } // end NormSetDefaultSyncPolicy() NORM_API_LINKAGE void NormSetDefaultNackingMode(NormSessionHandle sessionHandle, NormNackingMode nackingMode) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ReceiverSetDefaultNackingMode((NormObject::NackingMode)nackingMode); } // end NormSetDefaultNackingMode() NORM_API_LINKAGE void NormNodeSetNackingMode(NormNodeHandle nodeHandle, NormNackingMode nackingMode) { NormNode* node = (NormNode*)nodeHandle; if ((NULL != node) && (NormNode::SENDER == node->GetType())) { NormSenderNode* sender = static_cast(node); sender->SetDefaultNackingMode((NormObject::NackingMode)nackingMode); } } // end NormNodeSetNackingMode() NORM_API_LINKAGE void NormObjectSetNackingMode(NormObjectHandle objectHandle, NormNackingMode nackingMode) { NormObject* object = (NormObject*)objectHandle; if (object) object->SetNackingMode((NormObject::NackingMode)nackingMode); } // end NormObjectSetNackingMode() NORM_API_LINKAGE void NormSetDefaultRepairBoundary(NormSessionHandle sessionHandle, NormRepairBoundary repairBoundary) { NormSession* session = (NormSession*)sessionHandle; if (session) session->ReceiverSetDefaultRepairBoundary((NormSenderNode::RepairBoundary)repairBoundary); } // end NormSetDefaultRepairBoundary() NORM_API_LINKAGE void NormNodeSetRepairBoundary(NormNodeHandle nodeHandle, NormRepairBoundary repairBoundary) { NormNode* node = (NormNode*)nodeHandle; if ((NULL != node) && (NormNode::SENDER == node->GetType())) { NormSenderNode* sender = static_cast(node); sender->SetRepairBoundary((NormSenderNode::RepairBoundary)repairBoundary); } } // end NormNodeSetRepairBoundary() NORM_API_LINKAGE void NormSetDefaultRxRobustFactor(NormSessionHandle sessionHandle, int robustFactor) { NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; session->SetRxRobustFactor(robustFactor); instance->dispatcher.ResumeThread(); } } // end NormSetDefaultRxRobustFactor() NORM_API_LINKAGE void NormNodeSetRxRobustFactor(NormNodeHandle nodeHandle, int robustFactor) { if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { NormNode* node = (NormNode*)nodeHandle; if (NormNode::SENDER == node->GetType()) { NormSenderNode* sender = static_cast(node); sender->SetRobustFactor(robustFactor); } instance->dispatcher.ResumeThread(); } } } // end NormNodeSetRxRobustFactor() NORM_API_LINKAGE bool NormPreallocateRemoteSender(NormSessionHandle sessionHandle, unsigned long bufferSize, UINT16 segmentSize, UINT16 numData, UINT16 numParity, unsigned int streamBufferSize) { bool result = false; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { NormSession* session = (NormSession*)sessionHandle; result = session->PreallocateRemoteSender((unsigned int)bufferSize, segmentSize, numData, numParity, streamBufferSize); instance->dispatcher.ResumeThread(); } return result; } // end NormPreallocateRemoteSender() NORM_API_LINKAGE 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); result = stream->Read(buffer, numBytes); instance->dispatcher.ResumeThread(); } return result; } // end NormStreamRead() NORM_API_LINKAGE 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; result = stream->Read(NULL, &numBytes, true); instance->dispatcher.ResumeThread(); } return result; } // end NormStreamSeekMsgStart() NORM_API_LINKAGE UINT32 NormStreamGetReadOffset(NormObjectHandle streamHandle) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); if (stream) return stream->GetCurrentReadOffset(); else return 0; } // end NormStreamGetReadOffset() NORM_API_LINKAGE unsigned int NormStreamGetBufferUsage(NormObjectHandle streamHandle) { unsigned int usage = 0; NormInstance* instance = NormInstance::GetInstanceFromObject(streamHandle); if (instance && instance->dispatcher.SuspendThread()) { NormStreamObject* stream = static_cast((NormObject*)streamHandle); usage = stream->GetCurrentBufferUsage() ; instance->dispatcher.ResumeThread(); } return usage; } // end NormStreamGetBufferUsage() /** NORM Object Functions */ NORM_API_LINKAGE NormObjectType NormObjectGetType(NormObjectHandle objectHandle) { if (NORM_OBJECT_INVALID != objectHandle) return ((NormObjectType)(((NormObject*)objectHandle)->GetType())); else return NORM_OBJECT_NONE; } // end NormObjectGetType() NORM_API_LINKAGE bool NormObjectHasInfo(NormObjectHandle objectHandle) { return ((NormObject*)objectHandle)->HasInfo(); } // end NormObjectHasInfo() NORM_API_LINKAGE UINT16 NormObjectGetInfoLength(NormObjectHandle objectHandle) { return ((NormObject*)objectHandle)->GetInfoLength(); } // end NormObjectGetInfoLength() NORM_API_LINKAGE UINT16 NormObjectGetInfo(NormObjectHandle objectHandle, char* buffer, UINT16 bufferLen) { UINT16 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() NORM_API_LINKAGE NormSize NormObjectGetSize(NormObjectHandle objectHandle) { return ((NormSize)((NormObject*)objectHandle)->GetSize().GetOffset()); } // end NormObjectGetSize() NORM_API_LINKAGE NormSize NormObjectGetBytesPending(NormObjectHandle objectHandle) { NormSize bytesPending = 0; if (NORM_OBJECT_INVALID != objectHandle) { NormInstance* instance = NormInstance::GetInstanceFromObject(objectHandle); if (instance && instance->dispatcher.SuspendThread()) { bytesPending = ((NormSize)((NormObject*)objectHandle)->GetBytesPending().GetOffset()); instance->dispatcher.ResumeThread(); } } return bytesPending; } // end NormObjectGetBytesPending() NORM_API_LINKAGE void NormObjectCancel(NormObjectHandle objectHandle) { if (NORM_OBJECT_INVALID != objectHandle) { NormInstance* instance = NormInstance::GetInstanceFromObject(objectHandle); if (instance && instance->dispatcher.SuspendThread()) { NormObject* obj = (NormObject*)objectHandle; NormSenderNode* sender = obj->GetSender(); if (sender) sender->DeleteObject(obj); else obj->GetSession().DeleteTxObject(obj, false); instance->PurgeObjectNotifications(objectHandle); instance->dispatcher.ResumeThread(); } } } // end NormObjectCancel() NORM_API_LINKAGE void NormObjectSetUserData(NormObjectHandle objectHandle, const void* userData) { NormInstance* instance = NormInstance::GetInstanceFromObject(objectHandle); if (instance) { if (instance->dispatcher.SuspendThread()) { ((NormObject*)objectHandle)->SetUserData(userData); instance->dispatcher.ResumeThread(); } } } // end NormObjectSetUserData() NORM_API_LINKAGE const void* NormObjectGetUserData(NormObjectHandle objectHandle) { return (((NormObject*)objectHandle)->GetUserData()); } // end NormNormObjectGetUserData() NORM_API_LINKAGE void NormObjectRetain(NormObjectHandle objectHandle) { if (NORM_OBJECT_INVALID != objectHandle) { NormInstance* instance = NormInstance::GetInstanceFromObject(objectHandle); if (instance && instance->dispatcher.SuspendThread()) { ((NormObject*)objectHandle)->Retain(); instance->dispatcher.ResumeThread(); } } } // end NormRetainObject() NORM_API_LINKAGE void NormObjectRelease(NormObjectHandle objectHandle) { // (TBD) we could maintain separate "app_retain" and "interval_retain" // counts to prevent bad apps from messing up NORM interval code ??? if (NORM_OBJECT_INVALID != objectHandle) { NormInstance* instance = NormInstance::GetInstanceFromObject(objectHandle); if (instance && instance->dispatcher.SuspendThread()) { ((NormObject*)objectHandle)->Release(); instance->dispatcher.ResumeThread(); } } } // end NormObjectRelease() NORM_API_LINKAGE 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'; // (TBD) should we always do this result = true; } return result; } // end NormFileGetName() NORM_API_LINKAGE 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() NORM_API_LINKAGE const char* NormDataAccessData(NormObjectHandle dataHandle) { NormDataObject* dataObj = static_cast((NormObject*)dataHandle); return dataObj->GetData(); } // end NormDataAccessData() NORM_API_LINKAGE char* NormDataDetachData(NormObjectHandle dataHandle) { char* ptr = NULL; NormInstance* instance = NormInstance::GetInstanceFromObject(dataHandle); if (instance && instance->dispatcher.SuspendThread()) { NormDataObject* dataObj = static_cast((NormObject*)dataHandle); ptr = dataObj->DetachData(); instance->dispatcher.ResumeThread(); } return ptr; } // end NormDataDetachData() NORM_API_LINKAGE char* NormAlloc(size_t numBytes) { return new char[numBytes]; } // end NormAlloc() NORM_API_LINKAGE void NormFree(char* dataPtr) { if (NULL != dataPtr) delete[] dataPtr; } // end NormFree() NORM_API_LINKAGE NormNodeHandle NormObjectGetSender(NormObjectHandle objectHandle) { return (NormNodeHandle)(((NormObject*)objectHandle)->GetSender()); } // end NormObjectGetSender() /** NORM Node Functions */ NORM_API_LINKAGE NormNodeId NormNodeGetId(NormNodeHandle nodeHandle) { NormNode* node = (NormNode*)nodeHandle; if (NULL != node) return node->GetId(); else return NORM_NODE_NONE; } // end NormNodeGetId() NORM_API_LINKAGE bool NormNodeGetAddress(NormNodeHandle nodeHandle, char* addrBuffer, unsigned int* bufferLen, UINT16* 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 NormNodeGetAddress() NORM_API_LINKAGE void NormNodeSetUserData(NormNodeHandle nodeHandle, const void* userData) { if (NORM_NODE_INVALID != nodeHandle) { NormNode* node = (NormNode*)nodeHandle; return node->SetUserData(userData); } } NORM_API_LINKAGE const void* NormNodeGetUserData(NormNodeHandle nodeHandle) { NormNode* node = (NormNode*)nodeHandle; if (NULL != node) return node->GetUserData(); else return NULL; } // end NormNodeGetUserData() NORM_API_LINKAGE double NormNodeGetGrtt(NormNodeHandle nodeHandle) { NormNode* node = (NormNode*)nodeHandle; if ((NULL != node) && (NormNode::SENDER == node->GetType())) { NormSenderNode* sender = static_cast(node); sender->ResetGrttNotification(); return sender->GetGrttEstimate(); } else { return -1.0; } } // end NormNodeGetGrtt() NORM_API_LINKAGE bool NormNodeGetCommand(NormNodeHandle nodeHandle, char* cmdBuffer, unsigned int* cmdLength) { bool result = false; if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { NormNode* node = (NormNode*)nodeHandle; if (NormNode::SENDER == node->GetType()) { NormSenderNode* sender = static_cast(node); result = sender->ReadNextCmd(cmdBuffer, cmdLength); } instance->dispatcher.ResumeThread(); } } return result; } // end NormNodeGetCommand() NORM_API_LINKAGE bool NormNodeSendAckEx(NormNodeHandle nodeHandle, const char* buffer, unsigned int numBytes) { bool result = false; if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { NormNode* node = (NormNode*)nodeHandle; if (NormNode::SENDER == node->GetType()) { NormSenderNode* sender = static_cast(node); result = sender->SendAckEx(buffer, numBytes); } instance->dispatcher.ResumeThread(); } } return result; } // end NormNodeSendAckEx() NORM_API_LINKAGE bool NormNodeGetWatermarkEx(NormNodeHandle nodeHandle, char* buffer, unsigned int* buflen) { bool result = false; if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { NormNode* node = (NormNode*)nodeHandle; if (NormNode::SENDER == node->GetType()) { NormSenderNode* sender = static_cast(node); result = sender->GetWatermarkEx(buffer, buflen); } instance->dispatcher.ResumeThread(); } } return result; } // end NormNodeGetWatermarkEx() NORM_API_LINKAGE void NormNodeFreeBuffers(NormNodeHandle nodeHandle) { if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { NormNode* node = (NormNode*)nodeHandle; if (NormNode::SENDER == node->GetType()) { NormSenderNode* sender = static_cast(node); sender->FreeBuffers(); // Since this results in aborted objects, should we purge those object notifications? // or let the be delivered since the app may have associate state } instance->dispatcher.ResumeThread(); } } } // end NormNodeFreeBuffers() NORM_API_LINKAGE void NormNodeDelete(NormNodeHandle nodeHandle) { if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { NormNode* node = (NormNode*)nodeHandle; if (NormNode::SENDER == node->GetType()) { NormSenderNode* sender = static_cast(node); sender->GetSession().DeleteRemoteSender(*sender); } // else if NormNode::ACKER, should we remove from acking node list??? instance->PurgeNodeNotifications(nodeHandle); instance->dispatcher.ResumeThread(); } } } // end NormNodeDelete() NORM_API_LINKAGE void NormNodeRetain(NormNodeHandle nodeHandle) { if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { ((NormNode*)nodeHandle)->Retain(); instance->dispatcher.ResumeThread(); } } } // end NormNodeRetain() NORM_API_LINKAGE void NormNodeRelease(NormNodeHandle nodeHandle) { if (NORM_NODE_INVALID != nodeHandle) { NormInstance* instance = NormInstance::GetInstanceFromNode(nodeHandle); if (instance && instance->dispatcher.SuspendThread()) { ((NormNode*)nodeHandle)->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 ... // (Update: I've improved the way the NORM thread suspend/resume // is done so that this current approach is not very costly) // // Not sure why this function exists. It just counts the number // of RX_OBJECT_COMPLETED events currently in notification queue // which is a temporary and transitory thing??? NORM_API_LINKAGE UINT32 NormCountCompletedObjects(NormSessionHandle sessionHandle) { UINT32 result = 0; NormSession* session = (NormSession*)sessionHandle; NormInstance* instance = NormInstance::GetInstanceFromSession(sessionHandle); if (instance && instance->dispatcher.SuspendThread()) { result = instance->CountCompletedObjects(session); instance->dispatcher.ResumeThread(); } return result; } // end NormCountCompletedObjects()