From 7dd47241f8a2ec6b3788a18a1aaf084c5726f037 Mon Sep 17 00:00:00 2001 From: MattColegate Date: Fri, 30 Nov 2018 15:11:27 +0000 Subject: [PATCH 1/3] Allow semaphore reuse for z/OS --- src/ibmras/common/port/Semaphore.h | 15 +- src/ibmras/common/port/aix/Thread.cpp | 4 +- src/ibmras/common/port/linux/Thread.cpp | 4 +- src/ibmras/common/port/osx/Thread.cpp | 4 +- src/ibmras/common/port/windows/Thread.cpp | 4 +- src/ibmras/common/port/zos/Thread.cpp | 174 +++++++++++++++--- src/ibmras/common/util/FileUtils.cpp | 143 +++++++++++--- src/ibmras/common/util/FileUtils.h | 7 +- .../monitoring/agent/threads/WorkerThread.cpp | 3 +- 9 files changed, 284 insertions(+), 74 deletions(-) diff --git a/src/ibmras/common/port/Semaphore.h b/src/ibmras/common/port/Semaphore.h index 1e81e15..f059500 100644 --- a/src/ibmras/common/port/Semaphore.h +++ b/src/ibmras/common/port/Semaphore.h @@ -34,13 +34,16 @@ namespace port { /* class to provide semaphore semantics */ class Semaphore { public: - Semaphore(uint32 initial, uint32 max); /* semaphore initial and max count */ - void inc(); /* increase the semaphore count */ - bool wait(uint32 timeout); /* decrement the semaphore count */ - ~Semaphore(); /* OS cleanup of semaphore */ + Semaphore(uint32 initial, uint32 max, const char *sourceName); /* semaphore initial and max count, source name */ + void inc(); /* increase the semaphore count */ + bool wait(uint32 timeout); /* decrement the semaphore count */ + ~Semaphore(); /* OS cleanup of semaphore */ +#if defined(_ZOS) + int open(int* semid); /* Either create a new semaphore or open an existing one*/ +#endif private: - void* handle; /* opaque handle to platform data structure */ -#if defined __MACH__ + void* handle; /* opaque handle to platform data structure */ +#if defined __MACH__ || defined(_ZOS) std::string name; #endif }; diff --git a/src/ibmras/common/port/aix/Thread.cpp b/src/ibmras/common/port/aix/Thread.cpp index c77d51b..ec3db6f 100644 --- a/src/ibmras/common/port/aix/Thread.cpp +++ b/src/ibmras/common/port/aix/Thread.cpp @@ -143,10 +143,10 @@ void stopAllThreads() { pthread_mutex_unlock(&threadMapMux); } -Semaphore::Semaphore(uint32 initial, uint32 max) { +Semaphore::Semaphore(uint32 initial, uint32 max, const char* sourceName) { if (!stopping) { handle = new sem_t; - IBMRAS_DEBUG(fine,"in thread.cpp creating CreateSemaphoreA"); + IBMRAS_DEBUG_1(fine,"in thread.cpp creating semaphore for source %s", sourceName); int result; result = sem_init(reinterpret_cast(handle), 0, initial); if (result) { diff --git a/src/ibmras/common/port/linux/Thread.cpp b/src/ibmras/common/port/linux/Thread.cpp index f298e3e..ece0e8d 100644 --- a/src/ibmras/common/port/linux/Thread.cpp +++ b/src/ibmras/common/port/linux/Thread.cpp @@ -141,10 +141,10 @@ void stopAllThreads() { pthread_mutex_unlock(&threadMapMux); } -Semaphore::Semaphore(uint32 initial, uint32 max) { +Semaphore::Semaphore(uint32 initial, uint32 max, const char* sourceName) { if (!stopping) { handle = new sem_t; - IBMRAS_DEBUG(fine,"in thread.cpp creating CreateSemaphoreA"); + IBMRAS_DEBUG_1(fine,"in thread.cpp creating semaphore for source %s", sourceName); int result; result = sem_init(reinterpret_cast(handle), 0, initial); if (result) { diff --git a/src/ibmras/common/port/osx/Thread.cpp b/src/ibmras/common/port/osx/Thread.cpp index 9fe1915..098d7b9 100644 --- a/src/ibmras/common/port/osx/Thread.cpp +++ b/src/ibmras/common/port/osx/Thread.cpp @@ -155,14 +155,14 @@ void stopAllThreads() { pthread_mutex_unlock(&threadMapMux); } -Semaphore::Semaphore(uint32 initial, uint32 max) { +Semaphore::Semaphore(uint32 initial, uint32 max, const char* sourceName) { if (!stopping) { name = "/hc/"; name.append(ibmras::common::itoa(getpid())); name.append("/"); name.append(ibmras::common::itoa(pthread_self())); handle = new sem_t; - IBMRAS_DEBUG_1(fine, "in thread.cpp creating semaphore %s", name.c_str()); + IBMRAS_DEBUG_2(fine, "in thread.cpp creating semaphore %s for %s", name.c_str(), sourceName); handle = sem_open(name.c_str(), O_CREAT | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO, initial); int i=0; diff --git a/src/ibmras/common/port/windows/Thread.cpp b/src/ibmras/common/port/windows/Thread.cpp index 602dc74..f956523 100644 --- a/src/ibmras/common/port/windows/Thread.cpp +++ b/src/ibmras/common/port/windows/Thread.cpp @@ -64,9 +64,9 @@ void stopAllThreads() { IBMRAS_DEBUG(fine,"in thread.cpp->stopAllThreads"); } -Semaphore::Semaphore(uint32 initial, uint32 max) { +Semaphore::Semaphore(uint32 initial, uint32 max, const char* sourceName) { handle = new HANDLE; - IBMRAS_DEBUG(fine, "in thread.cpp creating CreateSemaphoreA"); + IBMRAS_DEBUG_1(fine,"in thread.cpp creating semaphore for source %s", sourceName); handle = CreateSemaphoreA(NULL, initial, max, NULL); if(handle == NULL) { IBMRAS_DEBUG_1(warning, "Failed to create semaphore : error code %d", GetLastError()); diff --git a/src/ibmras/common/port/zos/Thread.cpp b/src/ibmras/common/port/zos/Thread.cpp index a360ed4..470d623 100644 --- a/src/ibmras/common/port/zos/Thread.cpp +++ b/src/ibmras/common/port/zos/Thread.cpp @@ -33,9 +33,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -44,11 +46,22 @@ #include "ibmras/common/port/ThreadData.h" #include "ibmras/common/port/Semaphore.h" #include "ibmras/common/logging.h" +#include "ibmras/common/util/FileUtils.h" +#include "ibmras/monitoring/agent/Agent.h" namespace ibmras { namespace common { namespace port { +#define SEM_STORE_DIR ".com_ibm_tools_hc" +#define SEM_SUFFIX "_notifier" +#define SEMFLAGS_OPEN (S_IRUSR | S_IWUSR) +#define SEMFLAGS_CREATE (IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR) +#define SEM_CREATED 1 +#define SEM_OPENED 2 +#define SEM_OPEN_FAILED 0 +#define FILE_SEPARATOR "/" + IBMRAS_DEFINE_LOGGER("Port"); extern "C" void* wrapper(void *params) { @@ -164,48 +177,151 @@ int sem_timedwait(int *semid, struct timespec *t) { return 0; } -Semaphore::Semaphore(uint32 initial, uint32 max) { - IBMRAS_DEBUG(fine,"in thread.cpp creating CreateSemaphoreA"); - handle = new int*; - int result; - result = sem_init(reinterpret_cast(handle), 0, initial); - if (result) { - IBMRAS_DEBUG_1(warning, "Failed to create semaphore : error code %d", result); - handle = NULL; - } +bool getIPCKey(key_t* handle, std::string name) { + // get the semaphore temp dir set in the loader + std::string tempDir = ibmras::monitoring::agent::Agent::getInstance()->getProperty("platform_tempdir"); + if ("" == tempDir) { + IBMRAS_DEBUG(debug, "Defaulting tempDir to /tmp"); + tempDir = "/tmp"; + } + std::string semRecDir = tempDir + FILE_SEPARATOR + SEM_STORE_DIR; + // Create the semaphore resource directory if it doesn't exist + int directoryExists = ibmras::common::util::createDirectory(semRecDir); + IBMRAS_DEBUG_2(debug,"ibmras::common::util::createDirectory(%s) = %d", semRecDir.c_str(), directoryExists); + if (directoryExists) { + std::string baseFile = semRecDir + FILE_SEPARATOR + name + SEM_SUFFIX; + IBMRAS_DEBUG_1(debug, "baseFile = %s", baseFile.c_str()); + int fileExists = ibmras::common::util::createFile(baseFile); + IBMRAS_DEBUG_2(debug,"ibmras::common::util::createFile(%s) = %d", baseFile.c_str(), fileExists); + if (fileExists) { + uint8_t hc_id = 0x8c; // unique prefix to identify semaphore IDs as Health Centerol + /* Generate the key for creating the semaphore*/ + *handle = ftok(baseFile.c_str(), hc_id); + IBMRAS_DEBUG_2(info, "IPCkey for %s is %x", baseFile.c_str(), *handle); + if (-1 == *handle) { + IBMRAS_LOG_1(warning, "Unable to obtain semaphore IPC key: %s", strerror(errno)); + return false; + } else { + return true; + } + } else { + IBMRAS_LOG_1(warning, "Failed to create file %s; semaphore key not created", baseFile.c_str()); + return false; + } + } else { + IBMRAS_LOG_1(warning, "Failed to create directory %s; semaphore key not created", semRecDir.c_str()); + return false; + } +} + +Semaphore::Semaphore(uint32 initial, uint32 max, const char* sourceName) { + name = sourceName; + // handle will store the IPC key needed to open a semaphore + handle = new key_t*; + if (!getIPCKey((reinterpret_cast(handle)), name)) { + handle = NULL; + } +} + +int Semaphore::open(int* semid) { + IBMRAS_DEBUG_1(fine,"in thread.cpp creating semaphore for source %s", name.c_str()); + if (handle) { + int semflags_create; + int semflags_open; + uint32 permissions = 0666; + /* trim the permissions down to 9 least significant bits */ + permissions &= 0777; + semflags_open = SEMFLAGS_OPEN | permissions; + semflags_create = SEMFLAGS_CREATE | permissions; + // attempt to create semaphore + *semid = semget(*(reinterpret_cast(handle)), 1, semflags_create); + if (-1 == *semid) { + if (EEXIST == errno) { + IBMRAS_DEBUG(debug, "Semaphore already exists; attempt to open"); + *semid = semget(*(reinterpret_cast(handle)), 1, semflags_open); + } + if (-1 == *semid) { + IBMRAS_LOG_1(warning, "Unable to obtain semaphore: ", strerror(errno)); + return SEM_OPEN_FAILED; + } else { + IBMRAS_DEBUG_1(debug, "Semaphore %d opened", *semid); + return SEM_OPENED; + } + } else { + IBMRAS_DEBUG_1(debug, "Semaphore %d created", *semid); + return SEM_CREATED; + } + } else { + IBMRAS_LOG(warning, "Unable to obtain semaphore: invalid key"); + // attempt to generate a new key for next time + handle = new key_t*; + if (!getIPCKey((reinterpret_cast(handle)), name)) { + handle = NULL; + } + return SEM_OPEN_FAILED; + } } void Semaphore::inc() { - IBMRAS_DEBUG(finest, "Incrementing semaphore ticket count"); - if (handle) { - sem_post(reinterpret_cast(handle)); + int semid; + int result; + result = Semaphore::open(&semid); + if (result) { + if (SEM_CREATED == result) { + // shouldn't be incrementing a semaphore that doesn't already exist - probably shutting down + sem_destroy(&semid); + } else { + IBMRAS_DEBUG_2(finest, "Incrementing semaphore %d (%s)", semid, name.c_str()); + sem_post(&semid); + } } } bool Semaphore::wait(uint32 timeout) { + int semid; int result; struct timespec t; - while (!handle) { - sleep(timeout); /* wait for the semaphore to be established */ - } - - t.tv_sec = timeout; /* configure the sleep interval */ - t.tv_nsec = 0; - - result = sem_timedwait(reinterpret_cast(handle), &t); - if (!result) { - IBMRAS_DEBUG(finest, "semaphore posted"); - return true; - } - - IBMRAS_DEBUG(finest, "possible semaphore timeout"); - return (errno != EAGAIN); + result = Semaphore::open(&semid); + if (result) { + t.tv_sec = timeout; /* configure the sleep interval */ + t.tv_nsec = 0; + + result = sem_timedwait(&semid, &t); + if (!result) { + IBMRAS_DEBUG_2(finest, "Process %s waiting for semaphore %d", name.c_str(), semid); + return true; + } + IBMRAS_DEBUG_1(finest, "possible timeout for semaphore %d", semid); + return (errno != EAGAIN); + } else { + IBMRAS_LOG_1(warning, "Unable to obtain semaphore to wait on: %s", strerror(errno)); + return false; + } } Semaphore::~Semaphore() { - sem_destroy(reinterpret_cast(handle)); - delete (int*) handle; + int semid; + int result; + int n_count; + int z_count; + + result = Semaphore::open(&semid); + if (result) { + // how many processes are waiting for the semaphore? + n_count = semctl(semid, 0, GETNCNT); + z_count = semctl(semid, 0, GETZCNT); + if (-1 == n_count || -1 == z_count) { + IBMRAS_LOG_1(warning, "Unable to access semaphore info: %s", strerror(errno)); + } else { + // if we're the last semaphore users, no one should be waiting + if (0 == (n_count + z_count)) { + IBMRAS_DEBUG_2(debug, "Destroying semaphore %d for %s", semid, name.c_str()); + sem_destroy(&semid); + } + } + } + delete handle; } } diff --git a/src/ibmras/common/util/FileUtils.cpp b/src/ibmras/common/util/FileUtils.cpp index 4c79b61..9000663 100644 --- a/src/ibmras/common/util/FileUtils.cpp +++ b/src/ibmras/common/util/FileUtils.cpp @@ -29,37 +29,38 @@ #include #include #include +#include +#include #endif +#define BASEFILEPERM (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH) +#define BASEDIRPERM (S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH) + namespace ibmras { namespace common { namespace util { - - IBMRAS_DEFINE_LOGGER("FileUtils"); -bool ibmras::common::util::FileUtils::createDirectory(std::string& path) { - IBMRAS_DEBUG(debug, ">>>HLConnector::createDirectory"); +bool ibmras::common::util::createDirectory(std::string& path) { + IBMRAS_DEBUG(debug, ">>>FileUtils::createDirectory"); bool created = false; const char* pathName = path.c_str(); #if defined(WINDOWS) DWORD dirAttr; - IBMRAS_DEBUG_1(debug, "Creating directory: %s", pathName); dirAttr = GetFileAttributes(reinterpret_cast(pathName)); if(INVALID_FILE_ATTRIBUTES == dirAttr) { switch (GetLastError()) { case ERROR_PATH_NOT_FOUND: - IBMRAS_DEBUG(warning, "The directory was not found"); - IBMRAS_DEBUG_1(debug, "Creating directory: %s", pathName); + IBMRAS_DEBUG_1(fine, "Directory %s was not found - creating", pathName); if(!CreateDirectory(reinterpret_cast(pathName), NULL)) { switch (GetLastError()) { //if the directory already exists we will use it instead of the current one. case ERROR_ALREADY_EXISTS: - IBMRAS_DEBUG(warning, "The specified directory already exists."); + IBMRAS_DEBUG_1(fine, "Directory %s already exists.", pathName); created = true; break; case ERROR_PATH_NOT_FOUND: @@ -67,6 +68,7 @@ bool ibmras::common::util::FileUtils::createDirectory(std::string& path) { break; } } else { + IBMRAS_DEBUG_1(debug, "Directory %s created.", pathName); created = true; } break; @@ -77,12 +79,11 @@ bool ibmras::common::util::FileUtils::createDirectory(std::string& path) { IBMRAS_DEBUG(warning, "The network path was not found."); break; default: - IBMRAS_DEBUG(warning, "The directory could not be found, permissions?."); - IBMRAS_DEBUG_1(debug, "Creating directory: %s", pathName); + IBMRAS_DEBUG(fine, "Directory %s could not be found, permissions? Attempting creation.", pathName); if(!CreateDirectory(reinterpret_cast(pathName), NULL)) { switch (GetLastError()) { case ERROR_ALREADY_EXISTS: - IBMRAS_DEBUG(warning, "The specified directory already exists."); + IBMRAS_DEBUG_1(fine, "Directory %s already exists.", pathName); created = true; break; case ERROR_PATH_NOT_FOUND: @@ -90,42 +91,136 @@ bool ibmras::common::util::FileUtils::createDirectory(std::string& path) { break; } } else { + IBMRAS_DEBUG_1(debug, "Directory %s created.", pathName); created = true; } } - } + } else { + IBMRAS_DEBUG_1(debug, "Directory %s already exists", pathName); + created = true; + } #else struct stat dir; - IBMRAS_DEBUG_1(debug, "Pathname...%s\n", pathName); + IBMRAS_DEBUG_1(debug, "Pathname = %s", pathName); if (stat(pathName, &dir)) { - IBMRAS_DEBUG_1(debug, "Directory does not exist, creating...%s\n", pathName); - if (mkdir(pathName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) { - IBMRAS_DEBUG_1(debug, "Directory could not be created: ", strerror(errno)); + IBMRAS_DEBUG_1(fine, "Directory %s does not exist. Attempting creation", pathName); + if (-1 == mkdir(pathName, BASEDIRPERM)) { if(EEXIST == errno) { IBMRAS_DEBUG_1(debug, "Directory % already existed", pathName); created = true; - } + } else { + IBMRAS_DEBUG_2(warning, "Directory %s could not be created: %s", pathName, strerror(errno)); + } } else { - IBMRAS_DEBUG_1(debug, "Directory %s was created: ", pathName); + IBMRAS_DEBUG_1(debug, "Directory %s was created", pathName); + chmod(pathName,BASEDIRPERM); created = true; } } else { - IBMRAS_DEBUG(debug, "stat() returned 0, we'll check whether it was an existing directory"); + IBMRAS_DEBUG_1(fine, "stat() returned 0, checking whether %s is an existing directory", pathName); if(S_ISDIR(dir.st_mode)) { + IBMRAS_DEBUG_1(debug, "Directory %s does exist", pathName); + created = true; + } + } +#endif + IBMRAS_DEBUG(debug, "<<>>FileUtils::createFile(), path = %s", pathName); + +#if defined(WINDOWS) + HANDLE fileHandle; + DWORD fileAttr; + fileAttr = GetFileAttributes(reinterpret_cast(pathName)); + + if(INVALID_FILE_ATTRIBUTES == fileAttr) { + switch (GetLastError()) { + case ERROR_PATH_NOT_FOUND: + IBMRAS_DEBUG_1(fine, "File %s was not found. Attempting to create.", pathName); + fileHandle = CreateFile(reinterpret_cast(pathName), NULL, (GENERIC_READ | GENERIC_WRITE), 0, (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, CREATE_NEW, FILE_ATTRIBUTE_HIDDEN, NULL); + if(INVALID_HANDLE_VALUE == fileHandle) { + switch (GetLastError()) { + //if the directory already exists we will use it instead of the current one. + case ERROR_FILE_EXISTS: + IBMRAS_DEBUG_1(warning, "File %s already exists.", pathName); + created = true; + break; + case ERROR_PATH_NOT_FOUND: + IBMRAS_DEBUG_1(warning, "The system cannot find file %s.", pathName); + break; + } + } else { + created = true; + CloseHandle(fileHandle); + } + break; + case ERROR_INVALID_NAME: + IBMRAS_DEBUG(warning, "The filename, directory name, or volume label syntax is incorrect"); + break; + case ERROR_BAD_NETPATH: + IBMRAS_DEBUG(warning, "The network path was not found."); + break; + default: + IBMRAS_DEBUG_1(fine, "File %s could not be found, permissions? Attempting to create", pathName); + fileHandle = CreateFile(reinterpret_cast(pathName), NULL, (GENERIC_READ | GENERIC_WRITE), 0, (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, CREATE_NEW, FILE_ATTRIBUTE_HIDDEN, NULL); + if(INVALID_HANDLE_VALUE == fileHandle) { + switch (GetLastError()) { + case ERROR_FILE_EXISTS: + IBMRAS_DEBUG_1(fine, "File %s already exists.", pathName); + created = true; + break; + case ERROR_PATH_NOT_FOUND: + IBMRAS_DEBUG_1(warning, "The system cannot find file %s.", pathName); + break; + } + } else { + created = true; + CloseHandle(fileHandle); + } + } + } + +#else + struct stat file; + int fd; + if (stat(pathName, &file)) { + IBMRAS_DEBUG_1(debug, "File %s does not exist, attempting creation", pathName); + fd = open(pathName, (O_CREAT|O_EXCL|O_WRONLY), BASEFILEPERM); + if (-1 == fd) { + if(EEXIST == errno) { + IBMRAS_DEBUG_1(debug, "File %s already exists", pathName); + created = true; + } else { + IBMRAS_DEBUG_2(debug, "File %s could not be created: %s", pathName, strerror(errno)); + } + } else { + IBMRAS_DEBUG_1(debug, "File %s was created", pathName); + close(fd); + chmod(pathName, BASEFILEPERM); created = true; } + } else { + IBMRAS_DEBUG_1(debug, "stat() returned 0, checking whether %s is an existing file", pathName); + if(S_ISDIR(file.st_mode)) { + IBMRAS_DEBUG_1(warning, "File could not be created: %s is a directory", pathName); + } else { + IBMRAS_DEBUG_1(debug, "File %s does exist", pathName); + created = true; + } } #endif - IBMRAS_DEBUG(debug, "<<header.name), data(threadEntry, cleanUp), countdown(0) { source = pullSource; running = false; stopped = true; From c9341f26a0d7ab30279c0343c1f60b77e1d05bd0 Mon Sep 17 00:00:00 2001 From: MattColegate Date: Fri, 30 Nov 2018 15:36:02 +0000 Subject: [PATCH 2/3] Correct namespacing for FileUtils --- src/ibmras/common/util/FileUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ibmras/common/util/FileUtils.cpp b/src/ibmras/common/util/FileUtils.cpp index 9000663..ecd6464 100644 --- a/src/ibmras/common/util/FileUtils.cpp +++ b/src/ibmras/common/util/FileUtils.cpp @@ -42,7 +42,7 @@ namespace util { IBMRAS_DEFINE_LOGGER("FileUtils"); -bool ibmras::common::util::createDirectory(std::string& path) { +bool createDirectory(std::string& path) { IBMRAS_DEBUG(debug, ">>>FileUtils::createDirectory"); bool created = false; @@ -130,7 +130,7 @@ bool ibmras::common::util::createDirectory(std::string& path) { return created; } -bool ibmras::common::util::createFile(std::string& path) { +bool createFile(std::string& path) { bool created = false; const char* pathName = path.c_str(); IBMRAS_DEBUG_1(debug, ">>>FileUtils::createFile(), path = %s", pathName); From 13e6932c0a2fccb7c26e9dbb843f634893223ca0 Mon Sep 17 00:00:00 2001 From: MattColegate Date: Fri, 30 Nov 2018 15:49:02 +0000 Subject: [PATCH 3/3] Remove logger definition in WorkerThreads --- src/ibmras/monitoring/agent/threads/WorkerThread.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ibmras/monitoring/agent/threads/WorkerThread.cpp b/src/ibmras/monitoring/agent/threads/WorkerThread.cpp index 2f46dc1..5450b5e 100644 --- a/src/ibmras/monitoring/agent/threads/WorkerThread.cpp +++ b/src/ibmras/monitoring/agent/threads/WorkerThread.cpp @@ -25,7 +25,6 @@ namespace agent { namespace threads { extern IBMRAS_DECLARE_LOGGER; -IBMRAS_DEFINE_LOGGER("WorkerThread"); WorkerThread::WorkerThread(pullsource* pullSource) : semaphore(0, 1, pullSource->header.name), data(threadEntry, cleanUp), countdown(0) { @@ -102,4 +101,3 @@ void* WorkerThread::processLoop() { } } } /* end of namespace threads */ -