/** * @file kApiLib.cpp * * @internal * Copyright (C) 2012-2021 by LMI Technologies Inc. * Licensed under the MIT License. * Redistributed files must retain the above copyright notice. */ #define K_PLATFORM #include #include #include /* * Library configuration variables. See comments in kApiLib.x.h. */ kDx(kApiMemAllocFx) xkApiLib_memAllocFx = xkDefaultMemAlloc; kDx(kApiMemFreeFx) xkApiLib_memFreeFx = xkDefaultMemFree; kDx(kPointer) xkApiLib_memAllocProvider = kNULL; kDx(kApiMemSetFx) xkApiLib_memSetFx = kNULL; kDx(kApiMemCopyFx) xkApiLib_memCopyFx = kNULL; kDx(kApiMemCopyFx) xkApiLib_memMoveFx = kNULL; kDx(kApiAllocConstructFx) xkApiLib_appAllocConstructFx = xkAlloc_DefaultConstructAppAlloc; kDx(kApiAllocDestroyFx) xkApiLib_appAllocDestroyFx = xkAlloc_DefaultDestroyAppAlloc; kDx(kCallback) xkApiLib_earlyLogCallback = { 0, 0 }; kDx(kAtomic32s) xkApiLib_logInitialized = kFALSE; kDx(kApiLogfFx) xkApiLib_logfFx = kNULL; kDx(kApiTraceFx) xkApiLib_traceFx = kNULL; kDx(kApiAssertFx) xkApiLib_assertFx = xkDefaultAssert; kDx(kBool) xkApiLib_leakLoggingEnabled = kTRUE; kDx(kSize) xkApiLib_leaksDetected = 0; kDx(kBool) xkApiLib_checkTraceEnabled = kFALSE; kDx(k64u) xkApiLib_timerMultiplier = 1; kDx(k64u) xkApiLib_timerDivider = 1; kDx(kApiTimerQueryFx) xkApiLib_timerQueryFx = xkApiLib_TimerStub; kDx(k64u) xkApiLib_kernelTimerMultiplier = 1000; /* by default, assume kernel time unit is 1 ms */ kDx(k64u) xkApiLib_kernelTimerDivider = 1; kDx(kApiTimerScaleFx) xkApiLib_timerScaleFx = xkApiLib_DefaultTimerScaleFx; kDx(kSize) xkApiLib_threadStackSize = 0; kDx(kApiDateTimeQueryFx) xkApiLib_dateTimeQueryFx = xkDateTime_DefaultNow; kDx(kApiRandomFx) xkApiLib_randomFx = kNULL; kDx(kBool) xkApiLib_hasFileFx = kFALSE; kDx(kApiFileFx) xkApiLib_fileFx = { kNULL }; kDx(kBool) xkApiLib_hasDirectoryFx = kFALSE; kDx(kApiDirectoryFx) xkApiLib_directoryFx = { kNULL }; kDx(kChar) xkApiLib_nativeSeparator = 0; kDx(kBool) xkApiLib_networkInitializationEnabled = kTRUE; kDx(kApiLockFx) xkApiLib_symbolLockFx = kNULL; kDx(kApiUnlockFx) xkApiLib_symbolUnlockFx = kNULL; kDx(kPointer) xkApiLib_symbolLockProvider = kNULL; kDx(kBool) xkApiLib_symbolInitializationEnabled = kTRUE; kDx(kApiQueryNetInfoFx) xkApiLib_queryNetInfoFx = xkNetwork_DefaultQueryNetInfo; kDx(kApiQueryNetChangeFx) xkApiLib_queryNetChangeFx = xkNetwork_DefaultQueryNetChange; /* * Assembly types. */ kBeginAssemblyEx(k, kApiLib, kAPI_VERSION, kAPI_VERSION) //Interfaces kAddType(kCollection) kAddType(kCompressor) kAddType(kObjectPool) //Values kAddType(k16u) kAddType(k16s) kAddType(k32f) kAddType(k32u) kAddType(k32s) kAddType(k64f) kAddType(k64u) kAddType(k64s) kAddType(k8u) kAddType(k8s) kAddType(kAllocTrait) kAddType(kArgb) kAddType(kBool) kAddType(kByte) kAddType(kCallback) kAddType(kCallbackFx) kAddType(kCfa) kAddType(kChar) kAddType(kCipherPadding) kAddType(kCipherMode) kAddType(kComparison) kAddType(kCompressionPreset) kAddType(kCompressionType) kAddType(kDateTime) kAddType(kDateTimeFormat) kAddType(xkDat5SerializerTypeInfo) kAddType(xkDat6SerializerTypeInfo) kAddType(kDebugAllocation) kAddType(kEndianness) kAddType(kFileMode) kAddType(kHttpStatus) kAddType(kIpAddress) kAddType(kIpEndPoint) kAddType(kIpEntry) kAddType(kIpVersion) kAddType(kLockOption) kAddType(kLogArgs) kAddType(kLogOption) kAddType(kMacAddress) kAddType(kMemoryAlignment) kAddType(kMsgQueueItemOption) kAddType(kMsgQueuePurgeOption) kAddType(kParallelArgs) kAddType(kPipeStream) kAddType(kPixelFormat) kAddType(kPoint16s) kAddType(kPoint32s) kAddType(kPoint32f) kAddType(kPoint64f) kAddType(kPoint3d16s) kAddType(kPoint3d32s) kAddType(kPoint3d32f) kAddType(kPoint3d64f) kAddType(kPoint4d16s) kAddType(kPointer) kAddType(kProcess) kAddType(kRect16s) kAddType(kRect32s) kAddType(kRect32f) kAddType(kRect64f) kAddType(kRect3d64f) kAddType(kRgb) kAddType(kRotatedRect32s) kAddType(kRotatedRect32f) kAddType(kSeekOrigin) kAddType(xkSerializerWriteSection) kAddType(kSize) kAddType(kSSize) kAddType(kSocketType) kAddType(kSocketEvent) kAddType(kStatus) kAddType(kText16) kAddType(kText32) kAddType(kText64) kAddType(kText128) kAddType(kText256) kAddType(kThreadId) kAddType(kThreadPriorityClass) kAddType(kTimeSpan) kAddType(kTimeSpanFormat) kAddType(kValue) kAddType(kVersion) kAddType(kVoid) kAddType(kWebSocketDataType) kAddType(xkXmlAttrBlock) kAddType(xkXmlItemBlock) kAddBytes() //Classes kAddType(kAlloc) kAddType(kApiLog) kAddType(kArray1) kAddType(kArray2) kAddType(kArray3) kAddType(kArrayList) kAddType(kArrayProvider) kAddType(kAssembly) kAddType(kAtomic) kAddType(kBackTrace) kAddType(kBitArray) kAddType(kBlowfishCipher) kAddType(kBox) kAddType(kCipher) kAddType(kCipherStream) kAddType(kDat6Serializer) kAddType(kDat5Serializer) kAddType(xkDateTimeManager) kAddType(kDebugAlloc) kAddType(kDirectory) kAddType(kDynamicLib) kAddType(kEvent) kAddType(kFile) kAddType(kHash) kAddType(kHttpServer) kAddType(kHttpServerChannel) kAddType(kHttpServerRequest) kAddType(kHttpServerResponse) kAddType(kImage) kAddType(kList) kAddType(kLock) kAddType(kMap) kAddType(kMath) kAddType(kMemory) kAddType(kMsgQueue) kAddType(kNetwork) kAddType(kNetworkAdapter) kAddType(kNetworkInfo) kAddType(kNetworkInterface) kAddType(kObject) kAddType(kParallel) kAddType(kParallelJob) kAddType(kPath) kAddType(kPeriodic) kAddType(kPlugin) kAddType(kPoolAlloc) kAddType(kQueue) kAddType(kSemaphore) kAddType(kSerializer) kAddType(kSha1Hash) kAddType(kSocket) kAddType(kStream) kAddType(kString) kAddType(kSymbolInfo) kAddType(kTcpClient) kAddType(kTcpServer) kAddType(kThread) kAddType(kThreadPool) kAddType(kThreadPoolJob) kAddType(kTimer) kAddType(kType) kAddType(kUdpClient) kAddType(kUserAlloc) kAddType(kUtils) kAddType(kWebSocket) kAddType(kXml) //Initialization order kAddPriority(kAtomic) kAddPriority(kUtils) kAddPriority(kAlloc) kAddPriority(kAssembly) kEndAssemblyEx() /* * General library management functions. */ kFx(kStatus) kApiLib_SetMemAllocHandlers(kApiMemAllocFx allocFx, kApiMemFreeFx freeFx, kPointer provider) { xkApiLib_memAllocFx = allocFx; xkApiLib_memFreeFx = freeFx; xkApiLib_memAllocProvider = provider; return kOK; } kFx(kStatus) kApiLib_SetMemSetHandler(kApiMemSetFx function) { xkApiLib_memSetFx = function; return kOK; } kFx(kStatus) kApiLib_SetMemCopyHandler(kApiMemCopyFx function) { xkApiLib_memCopyFx = function; return kOK; } kFx(kStatus) kApiLib_SetMemMoveHandler(kApiMemCopyFx function) { xkApiLib_memMoveFx = function; return kOK; } kFx(kStatus) kApiLib_SetAppAllocConstructHandler(kApiAllocConstructFx function) { xkApiLib_appAllocConstructFx = function; return kOK; } kFx(kStatus) kApiLib_SetAppAllocDestroyHandler(kApiAllocDestroyFx function) { xkApiLib_appAllocDestroyFx = function; return kOK; } kFx(kStatus) kApiLib_SetLogfHandler(kApiLogfFx function) { xkApiLib_logfFx = function; return kOK; } kFx(kStatus) kApiLib_SetTraceHandler(kApiTraceFx function) { xkApiLib_traceFx = function; return kOK; } kFx(kPointer) xkApiLib_CastFailHandler(const kChar* file, k32s line) { kApiAssertFx handler = kApiLib_AssertHandler(); if (!kIsNull(handler)) { handler(file, (k32u)line); } else { abort(); } return kNULL; } kFx(kStatus) kApiLib_EnableLeakLogging(kBool enabled) { xkApiLib_leakLoggingEnabled = enabled; return kOK; } kFx(kStatus) kApiLib_SetDebugAllocListener(kCallbackFx function, kPointer receiver) { kAlloc alloc = kAlloc_App(); if (kObject_Is(alloc, kTypeOf(kDebugAlloc))) { kCheck(kDebugAlloc_SetAllocListener(alloc, function, receiver)); } return kOK; } kFx(kStatus) kApiLib_SetAssertHandler(kApiAssertFx function) { xkApiLib_assertFx = function; return kOK; } kFx(kStatus) kApiLib_SetTimerScale(k64u multiplier, k64u divider) { xkApiLib_timerMultiplier = multiplier; xkApiLib_timerDivider = divider; return kOK; } kFx(kStatus) kApiLib_SetTimerQueryHandler(kApiTimerQueryFx function) { xkApiLib_timerQueryFx = function; return kOK; } kFx(kStatus) kApiLib_SetKernelTimerScale(k64u multiplier, k64u divider) { k64u scale = multiplier / divider; k64u remainder = multiplier % divider; xkApiLib_timerScaleFx = xkApiLib_CustomTimerScaleFx; if (remainder == 0) { xkApiLib_kernelTimerMultiplier = scale; xkApiLib_kernelTimerDivider = 1; //optimized for most common case if (xkApiLib_kernelTimerMultiplier == 1000) { xkApiLib_timerScaleFx = xkApiLib_DefaultTimerScaleFx; } } else { xkApiLib_kernelTimerMultiplier = multiplier; xkApiLib_kernelTimerDivider = divider; } return kOK; } //optimized for most common case, in which timer scale is 1000 (1 kernel tick per ms) kFx(k32u) xkApiLib_DefaultTimerScaleFx(k64u time) { if (time == kINFINITE) { return kOS_INFINITE; } else { k64u ticks = time / 1000; k64u truncatedTime = 1000 * ticks; return (k32u) ((truncatedTime == time) ? ticks : ticks + 1); } } kFx(k32u) xkApiLib_CustomTimerScaleFx(k64u time) { if (time == kINFINITE) { return kOS_INFINITE; } else { k64u ticks = kApiLib_KernelTimerDivider()*(time) / kApiLib_KernelTimerMultiplier(); k64u truncatedTime = kApiLib_KernelTimerMultiplier()*(ticks) / kApiLib_KernelTimerDivider(); return (k32u) ((truncatedTime == time) ? ticks : ticks + 1); } } kFx(kStatus) kApiLib_SetDefaultStackSize(kSize stackSize) { xkApiLib_threadStackSize = stackSize; return kOK; } kFx(kStatus) kApiLib_SetDateTimeQueryHandler(kApiDateTimeQueryFx function) { xkApiLib_dateTimeQueryFx = kIsNull(function) ? xkDateTime_DefaultNow : function; return kOK; } kFx(kStatus) kApiLib_SetRandomHandler(kApiRandomFx function) { xkApiLib_randomFx = function; return kOK; } kFx(kStatus) kApiLib_SetFileHandlers(kApiFileFx* functions) { if (!xkApiLib_hasFileFx) { kZero(xkApiLib_fileFx); xkApiLib_hasFileFx = kTRUE; } return xkOverrideFunctions(&xkApiLib_fileFx, sizeof(xkApiLib_fileFx), functions); } kFx(kStatus) kApiLib_SetDirectoryHandlers(kApiDirectoryFx* functions) { if (!xkApiLib_hasDirectoryFx) { kZero(xkApiLib_directoryFx); xkApiLib_hasDirectoryFx = kTRUE; } return xkOverrideFunctions(&xkApiLib_directoryFx, sizeof(xkApiLib_directoryFx), functions); } kFx(kStatus) kApiLib_SetNativeSeparator(kChar separator) { xkApiLib_nativeSeparator = separator; return kOK; } kFx(kStatus) kApiLib_EnableNetworkInitialization(kBool enable) { xkApiLib_networkInitializationEnabled = enable; return kOK; } kFx(kStatus) kApiLib_SetSymbolLockHandlers(kApiLockFx lockFx, kApiUnlockFx unlockFx, kPointer provider) { xkApiLib_symbolLockFx = lockFx; xkApiLib_symbolUnlockFx = unlockFx; xkApiLib_symbolLockProvider = provider; return kOK; } kFx(kStatus) kApiLib_EnableSymbolInitialization(kBool enable) { xkApiLib_symbolInitializationEnabled = enable; return kOK; } kFx(kBool) kApiLib_HasTimerQueryHandler() { return xkApiLib_timerQueryFx != xkApiLib_TimerStub; } kFx(k64u) xkApiLib_TimerStub() { return 0; } kFx(kStatus) kApiLib_SetQueryNetInfoHandler(kApiQueryNetInfoFx function) { xkApiLib_queryNetInfoFx = function; return kOK; } kFx(kStatus) kApiLib_SetQueryNetChangeHandler(kApiQueryNetChangeFx function) { xkApiLib_queryNetChangeFx = function; return kOK; } /* * Logging infrastructure */ typedef struct kApiLogStatic { kLock lock; //Provides exclusive acccess. kEvent listeners; //List of log handlers. -- kEvent> kEvent formatters; //List of log formatters. -- kEvent> kString formatterContent[2]; //Temporary strings, used for message formatting. } kApiLogStatic; kDeclareStaticClassEx(k, kApiLog) kFx(kStatus) xkApiLog_InitStatic(); kFx(kStatus) xkApiLog_ReleaseStatic(); kBeginStaticClassEx(k, kApiLog) kEndStaticClassEx() kFx(kStatus) xkApiLog_InitStatic() { kStaticObj(kApiLog); kAlloc allocator = kAlloc_App(); kStatus status; kTry { kTest(kLock_ConstructEx(&sobj->lock, xkLOCK_OPTION_PRIORITY_INHERITANCE, allocator)); kTest(kEvent_Construct(&sobj->listeners, allocator)); kTest(kEvent_Construct(&sobj->formatters, allocator)); kTest(kString_Construct(&sobj->formatterContent[0], "", allocator)); kTest(kString_Construct(&sobj->formatterContent[1], "", allocator)); if (!kIsNull(xkApiLib_earlyLogCallback.function)) { kEvent_Add(sobj->listeners, xkApiLib_earlyLogCallback.function, xkApiLib_earlyLogCallback.receiver); } //there shouldn't be any multithreaded access at this point; using atomic out of abundance of caution kAtomic32s_Exchange(&xkApiLib_logInitialized, kTRUE); } kCatch(&status) { xkApiLog_ReleaseStatic(); kEndCatch(status); } return kOK; } kFx(kStatus) xkApiLog_ReleaseStatic() { kStaticObj(kApiLog); //there shouldn't be any multithreaded access at this point; using atomic out of abundance of caution kAtomic32s_Exchange(&xkApiLib_logInitialized, kFALSE); kCheck(kDestroyRef(&sobj->formatterContent[0])); kCheck(kDestroyRef(&sobj->formatterContent[1])); kCheck(kDestroyRef(&sobj->formatters)); kCheck(kDestroyRef(&sobj->listeners)); kCheck(kDestroyRef(&sobj->lock)); return kOK; } kFx(kStatus) kApiLib_AddLogHandler(kApiLogFx function, kPointer receiver) { if (!kAtomic32s_Get(&xkApiLib_logInitialized)) { kCheckState(kIsNull(xkApiLib_earlyLogCallback.function)); xkApiLib_earlyLogCallback.function = (kCallbackFx) function; xkApiLib_earlyLogCallback.receiver = receiver; } else { kStaticObj(kApiLog); kLock_Enter(sobj->lock); kTry { kTest(kEvent_Add(sobj->listeners, (kCallbackFx) function, receiver)); } kFinally { kLock_Exit(sobj->lock); kEndFinally(); } } return kOK; } kFx(kStatus) kApiLib_RemoveLogHandler(kApiLogFx function, kPointer receiver) { if (!kAtomic32s_Get(&xkApiLib_logInitialized)) { if ((xkApiLib_earlyLogCallback.function == (kCallbackFx)function) && (xkApiLib_earlyLogCallback.receiver == receiver)) { xkApiLib_earlyLogCallback.function = kNULL; xkApiLib_earlyLogCallback.receiver = kNULL; } } else { kStaticObj(kApiLog); kLock_Enter(sobj->lock); kTry { kTest(kEvent_Remove(sobj->listeners, (kCallbackFx)function, receiver)); if ((xkApiLib_earlyLogCallback.function == (kCallbackFx)function) && (xkApiLib_earlyLogCallback.receiver == receiver)) { xkApiLib_earlyLogCallback.function = kNULL; xkApiLib_earlyLogCallback.receiver = kNULL; } } kFinally { kLock_Exit(sobj->lock); kEndFinally(); } } return kOK; } kFx(kStatus) kApiLib_AddLogFormatter(kApiLogFormatterFx function, kPointer receiver) { kCheckState(kAtomic32s_Get(&xkApiLib_logInitialized)); { kStaticObj(kApiLog); kLock_Enter(sobj->lock); kTry { kTest(kEvent_Add(sobj->formatters, (kCallbackFx)function, receiver)); } kFinally { kLock_Exit(sobj->lock); kEndFinally(); } } return kOK; } kFx(kStatus) kApiLib_RemoveLogFormatter(kApiLogFormatterFx function, kPointer receiver) { kCheckState(kAtomic32s_Get(&xkApiLib_logInitialized)); { kStaticObj(kApiLog); kLock_Enter(sobj->lock); kTry { kTest(kEvent_Remove(sobj->formatters, (kCallbackFx)function, receiver)); } kFinally { kLock_Exit(sobj->lock); kEndFinally(); } } return kOK; } kFx(kStatus) kApiLib_InvokeLegacyLogHandler(const kChar* format, ...) { if (!kIsNull(xkApiLib_logfFx)) { kVarArgList args; kVarArgList_Start(args, format); { xkApiLib_logfFx(format, args); } kVarArgList_End(args); } return kOK; } kFx(kStatus) kApiLib_InvokeEarlyLogHandler(const kLogArgs* args) { kApiLogFx function = (kApiLogFx)xkApiLib_earlyLogCallback.function; if (!kIsNull(function)) { function(xkApiLib_earlyLogCallback.receiver, args); } return kOK; } kFx(const kChar*) kApiLib_InvokeLogFormatters(const kChar* message) { kStaticObj(kApiLog); kList formatterList = kEvent_Listeners(sobj->formatters); kListItem it = kList_First(formatterList); kSize formatContentIndex = 0; //copy to buffer kString_Set(sobj->formatterContent[0], message); //apply any formatters while (!kIsNull(it)) { const kCallback* formatter = kList_AtT(formatterList, it, kCallback); kApiLogFormatterFx function = (kApiLogFormatterFx)formatter->function; kString input = sobj->formatterContent[formatContentIndex % 2]; kString output = sobj->formatterContent[(formatContentIndex + 1) % 2]; kString_Clear(output); function(formatter->receiver, input, output); it = kList_Next(formatterList, it); formatContentIndex++; } //return the final, formatted result return kString_Chars(sobj->formatterContent[formatContentIndex%2]); } kFx(kStatus) kApiLib_InvokeLogListeners(const kLogArgs* args) { kStaticObj(kApiLog); kList listenerList = kEvent_Listeners(sobj->listeners); kListItem it = kList_First(listenerList); while (!kIsNull(it)) { const kCallback* listener = kList_AtT(listenerList, it, kCallback); kApiLogFx function = (kApiLogFx) listener->function; function(listener->receiver, args); it = kList_Next(listenerList, it); } return kOK; } kFx(kStatus) xkApiLib_Log(const kLogArgs* args) { //legacy support -- call the legacy log hander, if one exists kApiLib_InvokeLegacyLogHandler("%s", args->message); if (!kAtomic32s_Get(&xkApiLib_logInitialized)) { //if log service not yet initialized, call early log handler (if registered) kApiLib_InvokeEarlyLogHandler(args); } else { kStaticObj(kApiLog); kLock_Enter(sobj->lock); { if (kEvent_Count(sobj->formatters) == 0) { kApiLib_InvokeLogListeners(args); } else { kLogArgs argsFormatted = { 0 }; //call formatters, providing opportunity to transform buffer const kChar* formattedMessage = kApiLib_InvokeLogFormatters(args->message); argsFormatted.message = formattedMessage; argsFormatted.options = args->options; argsFormatted.source = args->source; argsFormatted.dateTime = args->dateTime; argsFormatted.upTime = args->upTime; //notify listeners with final formatted content kApiLib_InvokeLogListeners(&argsFormatted); } } kLock_Exit(sobj->lock); } return kOK; }