From 81a9d08665867b5bd6ff20b15dfd2a511c7382eb Mon Sep 17 00:00:00 2001 From: Chris <34682781+monkins1010@users.noreply.github.com> Date: Mon, 26 Nov 2018 20:42:28 +0000 Subject: [PATCH] added multi gpu mining --- kernel.cu | 797 ++++++++++++------------- libs/pthreadVC2.lib | Bin 0 -> 166580 bytes pthreads/pthread.h | 1372 +++++++++++++++++++++++++++++++++++++++++++ pthreads/sched.h | 183 ++++++ 4 files changed, 1926 insertions(+), 426 deletions(-) create mode 100644 libs/pthreadVC2.lib create mode 100644 pthreads/pthread.h create mode 100644 pthreads/sched.h diff --git a/kernel.cu b/kernel.cu index 471656e..59f17ec 100644 --- a/kernel.cu +++ b/kernel.cu @@ -4,7 +4,7 @@ #include #include #include - +#include #include #include "UCPClient.h" @@ -29,15 +29,47 @@ // #pragma comment(lib, "nvapi.lib") // #pragma comment(lib, "nvapi64.lib") +void promptExit(int exitCode); +pthread_mutex_t stratum_sock_lock; +pthread_mutex_t stratum_log_lock; + #ifdef __INTELLISENSE__ #define __launch_bounds__(blocksize) #endif //#define ROTR64(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) #define ROTR(x,n) ROTR64(x,n) +#define MAX_GPUS 16 +#define CUDA_SAFE_CALL(call) \ +do { \ + cudaError_t err = call; \ + if (cudaSuccess != err) { \ + fprintf(stderr, "Cuda error in func '%s' at line %i : %s.\n", \ + __FUNCTION__, __LINE__, cudaGetErrorString(err) ); \ + promptExit(-1); \ + } \ +} while (0) + __constant__ static uint64_t __align__(8) c_512[16]; __constant__ static uint64_t __align__(8) c_vblake[8]; +__constant__ uint64_t headerIn[8]; +static uint32_t *d_nonces[MAX_GPUS]; +static uint64_t *dev_nonceStart[MAX_GPUS]; + + +__host__ +void veri_init(int thr_id) +{ + CUDA_SAFE_CALL(cudaMalloc(&d_nonces[thr_id], 1 * sizeof(uint32_t))); + CUDA_SAFE_CALL(cudaMalloc(&dev_nonceStart[thr_id], 1 * sizeof(uint64_t))); +}; +void veri_setBlock(void *blockf) +{ + + CUDA_SAFE_CALL(cudaMemcpyToSymbol(headerIn, blockf, 8 * sizeof(uint64_t), 0, cudaMemcpyHostToDevice)); +}; + __device__ __forceinline__ uint64_t ROTR64_L(uint64_t value, const int offset) { uint2 result; @@ -78,13 +110,9 @@ __device__ __forceinline__ uint64_t ROTR64_H(uint64_t value, v[d] ^= (~v[a] & ~v[b] & ~v[c]) | (~v[a] & v[b] & v[c]) | (v[a] & ~v[b] & v[c]) | (v[a] & v[b] & ~v[c]); \ v[d] ^= (~v[a] & ~v[b] & v[c]) | (~v[a] & v[b] & ~v[c]) | (v[a] & ~v[b] & ~v[c]) | (v[a] & v[b] & v[c]); \ } -cudaStream_t cudastream; -uint32_t *blockHeadermobj = nullptr; -uint32_t *midStatemobj = nullptr; -uint32_t *nonceOutmobj = nullptr; -cudaError_t grindNonces(uint32_t *nonceResult, uint64_t *hashStart, const uint64_t *header); +void grindNonces(uint32_t startNonce, uint32_t *nonceResult, uint64_t *hashStart, const uint64_t *header, int dev_id); __device__ __constant__ static const uint8_t c_sigma_big[16][16] = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, @@ -205,8 +233,8 @@ uint64_t vBlake2(const uint64_t h0, const uint64_t h1, const uint64_t h2, const #endif #if HIGH_RESOURCE -#define DEFAULT_BLOCKSIZE 0xd0000 -#define DEFAULT_THREADS_PER_BLOCK 1024 +#define DEFAULT_BLOCKSIZE 0x80000 +#define DEFAULT_THREADS_PER_BLOCK 256 #else #define DEFAULT_BLOCKSIZE 512 #define DEFAULT_THREADS_PER_BLOCK 512 @@ -214,23 +242,39 @@ uint64_t vBlake2(const uint64_t h0, const uint64_t h1, const uint64_t h2, const int blocksize = DEFAULT_BLOCKSIZE; int threadsPerBlock = DEFAULT_THREADS_PER_BLOCK; +int opt_n_threads = 0; +short device_map[MAX_GPUS] = { 0 }; +int gpu_threads = 1; +int active_gpus; +char * device_name[MAX_GPUS]; +long device_sm[MAX_GPUS] = { 0 }; +short device_mpcount[MAX_GPUS] = { 0 }; +int init[MAX_GPUS] = { 0 }; bool verboseOutput = false; +struct mining_attr { + int dev_id; + string host; + int port; + string username; + string password; + +}; /* * Kernel function to search a range of nonces for a solution falling under the macro-configured difficulty (CPU=2^24, GPU=2^32). */ -//__launch_bounds__(256, 2) -__global__ void vblakeHasher(uint32_t *nonceStart, uint32_t *nonceOut, uint64_t *hashStartOut, uint64_t const *headerIn) +__global__ void vblakeHasher(uint32_t startnonce, uint32_t *nonceOut, uint64_t *hashStartOut) { // Generate a unique starting nonce for each thread that doesn't overlap with the work of any other thread - const uint32_t workStart = ((blockDim.x * blockIdx.x + threadIdx.x)) + nonceStart[0]; + uint32_t nonce = (blockDim.x * blockIdx.x + threadIdx.x) + startnonce; __shared__ uint64_t s_u512[16],s_vblake[8]; if (threadIdx.x < 16U) s_u512[threadIdx.x] = c_512[threadIdx.x]; if (threadIdx.x < 8U) s_vblake[threadIdx.x] = c_vblake[threadIdx.x]; + uint64_t nonceHeaderSection = headerIn[7]; - unsigned int nonce = workStart; + //for (unsigned int nonce = workStart; nonce < workStart + WORK_PER_THREAD; nonce++) { // Zero out nonce position and write new nonce to last 32 bits of prototype header nonceHeaderSection &= 0x00000000FFFFFFFFu; @@ -250,7 +294,28 @@ __global__ void vblakeHasher(uint32_t *nonceStart, uint32_t *nonceOut, uint64_t } //} } +int cuda_num_devices() +{ + int version = 0, GPU_N = 0; + cudaError_t err = cudaDriverGetVersion(&version); + if (err != cudaSuccess) { + printf("Unable to query CUDA driver version! Is an nVidia driver installed?\n"); + exit(1); + } + + if (version < CUDART_VERSION) { + printf("Your system does not support CUDA %d.%d API!\n", + CUDART_VERSION / 1000, (CUDART_VERSION % 1000) / 10); + exit(1); + } + err = cudaGetDeviceCount(&GPU_N); + if (err != cudaSuccess) { + printf("Unable to query number of CUDA devices! Is an nVidia driver installed?\n"); + exit(1); + } + return GPU_N; +} void promptExit(int exitCode) { cout << "Exiting in 10 seconds..." << endl; @@ -272,12 +337,12 @@ void embedTimestampInHeader(uint8_t *header, uint32_t timestamp) /** * Returns a 64-byte header to attempt to mine with. */ -uint64_t* getWork(UCPClient& ucpClient, uint32_t timestamp) +void getWork(UCPClient& ucpClient, uint32_t timestamp, uint64_t *header) { - uint64_t *header = new uint64_t[8]; + //uint64_t *header = new uint64_t[8]; ucpClient.copyHeaderToHash((byte *)header); embedTimestampInHeader((uint8_t*)header, timestamp); - return header; + //return header; } int deviceToUse = 0; @@ -330,7 +395,36 @@ void vprintf(char* toprint) { printf(toprint); } } +void cuda_devicenames() +{ + cudaError_t err; + int GPU_N; + err = cudaGetDeviceCount(&GPU_N); + if (err != cudaSuccess) + { + printf("Unable to query number of CUDA devices! Is an nVidia driver installed?"); + exit(1); + } + if (opt_n_threads) + GPU_N = min(MAX_GPUS, opt_n_threads); + for (int i = 0; i < GPU_N; i++) + { + int dev_id = device_map[i]; + cudaDeviceProp props; + cudaGetDeviceProperties(&props, dev_id); + + device_sm[dev_id] = (props.major * 100 + props.minor * 10); + device_mpcount[dev_id] = (short)props.multiProcessorCount; + + if (device_name[dev_id]) { + free(device_name[dev_id]); + device_name[dev_id] = NULL; + } + + device_name[dev_id] = strdup(props.name); + } +} void printHelpAndExit() { printf("VeriBlock vBlake GPU CUDA Miner v1.0\n"); printf("Required Arguments:\n"); @@ -368,6 +462,38 @@ void net_deinit(void) WSACleanup(); #endif } +static bool substringsearch(const char *haystack, const char *needle, int &match) +{ + int hlen = (int)strlen(haystack); + int nlen = (int)strlen(needle); + for (int i = 0; i < hlen; ++i) + { + if (haystack[i] == ' ') continue; + int j = 0, x = 0; + while (j < nlen) + { + if (haystack[i + x] == ' ') { ++x; continue; } + if (needle[j] == ' ') { ++j; continue; } + if (needle[j] == '#') return ++match == needle[j + 1] - '0'; + if (tolower(haystack[i + x]) != tolower(needle[j])) break; + ++j; ++x; + } + if (j == nlen) return true; + } + return false; +} +int cuda_finddevice(char *name) +{ + int num = cuda_num_devices(); + int match = 0; + for (int i = 0; i < num; ++i) + { + cudaDeviceProp props; + if (cudaGetDeviceProperties(&props, i) == cudaSuccess) + if (substringsearch(props.name, name, match)) return i; + } + return -1; +} string net_dns_resolve(const char* hostname) { @@ -420,6 +546,142 @@ string net_dns_resolve(const char* hostname) } char outputBuffer[8192]; + + +void* miner_thread(void* arg){ + // Run initialization of device before beginning timer + struct mining_attr *arg_Struct = + (struct mining_attr*) arg; + + pthread_mutex_lock(&stratum_sock_lock); + UCPClient ucpClient(arg_Struct->host, arg_Struct->port, arg_Struct->username, arg_Struct->password); + + byte target[24]; + ucpClient.copyMiningTarget(target); + uint64_t header[8]; + + getWork(ucpClient, (uint32_t)std::time(0),header); + pthread_mutex_unlock(&stratum_sock_lock); + + pthread_mutex_lock(&stratum_log_lock); + unsigned long long startTime = std::time(0); + pthread_mutex_unlock(&stratum_log_lock); + //mutex unlock + + uint32_t nonceResult[1] = { 0 }; + uint64_t hashStart[1] = { 0 }; + uint32_t startNonce = 0; + unsigned long long hashes = 0; + uint32_t count = 0; + int numLines = 0; + + // Mining loop + while (true) { + vprintf("top of mining loop\n"); + count++; + long timestamp = (long)std::time(0); + //delete[] header; + vprintf("Getting work...\n"); + + pthread_mutex_lock(&stratum_sock_lock); + getWork(ucpClient, timestamp, header); + vprintf("Getting job id...\n"); + int jobId = ucpClient.getJobId(); + pthread_mutex_unlock(&stratum_sock_lock); + + count++; + vprintf("Running kernel...\n"); + grindNonces(startNonce, nonceResult, hashStart, header, arg_Struct->dev_id); + + vprintf("Kernel finished...\n"); + + //mutex lock + pthread_mutex_lock(&stratum_log_lock); + unsigned long long totalTime = std::time(0) - startTime; + pthread_mutex_unlock(&stratum_log_lock); + //todo mutex unlock + hashes += (blocksize * threadsPerBlock * WORK_PER_THREAD); + if ((uint64_t)startNonce + (uint64_t)(blocksize * threadsPerBlock * WORK_PER_THREAD) < (uint64_t)0xffffffff) { + startNonce += (blocksize * threadsPerBlock * WORK_PER_THREAD); + } + else + startNonce = 0; + + double hashSpeed = (double)hashes; + hashSpeed /= (totalTime * 1024 * 1024); + + if (count % 10 == 0) { + //mutex lock + pthread_mutex_lock(&stratum_sock_lock); + + int validShares = ucpClient.getValidShares(); + int invalidShares = ucpClient.getInvalidShares(); + int totalAccountedForShares = invalidShares + validShares; + int totalSubmittedShares = ucpClient.getSentShares(); + int unaccountedForShares = totalSubmittedShares - totalAccountedForShares; + pthread_mutex_unlock(&stratum_sock_lock); + //mutex unlock + double percentage = ((double)validShares) / totalAccountedForShares; + percentage *= 100; + // printf("[GPU #%d (%s)] : %f MH/second valid shares: %d/%d/%d (%.3f%%)\n", deviceToUse, selectedDeviceName.c_str(), hashSpeed, validShares, totalAccountedForShares, totalSubmittedShares, percentage); + + printf("[GPU: %d %s] : %0.2f MH/s shares: %d/%d/%d (%.3f%%)\n", arg_Struct->dev_id, device_name[arg_Struct->dev_id], hashSpeed, validShares, totalAccountedForShares, totalSubmittedShares, percentage); + } + + if (nonceResult[0] != 0x01000000 && nonceResult[0] != 0) { + uint32_t nonce = *nonceResult; + nonce = (((nonce & 0xFF000000) >> 24) | ((nonce & 0x00FF0000) >> 8) | ((nonce & 0x0000FF00) << 8) | ((nonce & 0x000000FF) << 24)); + + pthread_mutex_lock(&stratum_sock_lock); + ucpClient.submitWork(jobId, timestamp, nonce); + pthread_mutex_unlock(&stratum_sock_lock); + + nonceResult[0] = 0; + + char line[100]; + + // Hash coming from GPU is reversed + uint64_t hashFlipped = 0; + hashFlipped |= (hashStart[0] & 0x00000000000000FF) << 56; + hashFlipped |= (hashStart[0] & 0x000000000000FF00) << 40; + hashFlipped |= (hashStart[0] & 0x0000000000FF0000) << 24; + hashFlipped |= (hashStart[0] & 0x00000000FF000000) << 8; + hashFlipped |= (hashStart[0] & 0x000000FF00000000) >> 8; + hashFlipped |= (hashStart[0] & 0x0000FF0000000000) >> 24; + hashFlipped |= (hashStart[0] & 0x00FF000000000000) >> 40; + hashFlipped |= (hashStart[0] & 0xFF00000000000000) >> 56; + +#if CPU_SHARES + sprintf(line, "\t Share Found @ 2^24! {%#018llx} [nonce: %#08lx]", hashFlipped, nonce); +#else + sprintf(line, "\t Share Found @ 2^32! {%#018llx} [nonce: %#08lx]", hashFlipped, nonce); +#endif + + cout << line << endl; + vprintf("Logging\n"); + Log::info(line); + vprintf("Done logging\n"); + vprintf("Made line\n"); + + numLines++; + + // Uncomment these lines to get access to this data for display purposes + /* + long long extraNonce = ucpClient.getStartExtraNonce(); + int jobId = ucpClient.getJobId(); + int encodedDifficulty = ucpClient.getEncodedDifficulty(); + string previousBlockHashHex = ucpClient.getPreviousBlockHash(); + string merkleRoot = ucpClient.getMerkleRoot(); + */ + + } + vprintf("About to restart loop...\n"); + } + + printf("Resetting device...\n"); + CUDA_SAFE_CALL(cudaDeviceReset()); + +} int main(int argc, char *argv[]) { // Check for help argument (only -h) @@ -450,13 +712,48 @@ int main(int argc, char *argv[]) printf("%s\n", argument); if (argument[0] == '-' && argument[1] == 'd') { - if (strlen(argv[i + 1]) == 2) { - // device num >= 10 - deviceToUse = (argv[i + 1][0] - 48) * 10 + (argv[i + 1][1] - 48); + + int device_thr[MAX_GPUS] = { 0 }; + int ngpus = cuda_num_devices(); + char* pch = strtok(argv[i + 1], ","); + opt_n_threads = 0; + while (pch != NULL && opt_n_threads < MAX_GPUS) { + if (pch[0] >= '0' && pch[0] <= '9' && strlen(pch) <= 2) + { + if (atoi(pch) < ngpus) + device_map[opt_n_threads++] = atoi(pch); + else { + printf("Non-existant CUDA device #%d specified in -d option\n\n", atoi(pch)); + printHelpAndExit(); + } + } + else { + int device = cuda_finddevice(pch); + if (device >= 0 && device < ngpus) + device_map[opt_n_threads++] = device; + else { + printf("Non-existant CUDA device '%s' specified in -d option\n\n", pch); + printHelpAndExit(); + } + } + pch = strtok(NULL, ","); + } + // count threads per gpu + for (int n = 0; n < opt_n_threads; n++) { + int device = device_map[n]; + device_thr[device]++; } - else { - deviceToUse = argv[i + 1][0] - 48; + for (int n = 0; n < ngpus; n++) { + gpu_threads = max(gpu_threads, device_thr[n]); } + + // if (strlen(argv[i + 1]) == 2) { + // device num >= 10 + // deviceToUse = (argv[i + 1][0] - 48) * 10 + (argv[i + 1][1] - 48); + // } + // else { + // deviceToUse = argv[i + 1][0] - 48; + // } } else if (!strcmp(argument, "-o")) { @@ -516,6 +813,10 @@ int main(int argc, char *argv[]) printHelpAndExit(); } + pthread_mutex_init(&stratum_sock_lock, NULL); + pthread_mutex_init(&stratum_log_lock, NULL); + + if (HIGH_RESOURCE) { sprintf(outputBuffer, "Resource Utilization: HIGH"); cerr << outputBuffer << endl; @@ -527,16 +828,6 @@ int main(int argc, char *argv[]) Log::info(outputBuffer); } - if (NVML) { - sprintf(outputBuffer, "NVML Status: ENABLED"); - cerr << outputBuffer << endl; - Log::info(outputBuffer); - } - else { - sprintf(outputBuffer, "NVML Status: DISABLED"); - cerr << outputBuffer << endl; - Log::info(outputBuffer); - } if (CPU_SHARES) { sprintf(outputBuffer, "Share Type: CPU"); @@ -560,8 +851,7 @@ int main(int argc, char *argv[]) Log::info(outputBuffer); } - // No effect if NVML is not enabled - readyNVML(deviceToUse); + #ifdef _WIN32 HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); @@ -603,426 +893,81 @@ int main(int argc, char *argv[]) sprintf(outputBuffer, "Attempting to mine to pool %s:%d with username %s and password %s...", host.c_str(), port, username.c_str(), password.c_str()); cout << outputBuffer << endl; Log::info(outputBuffer); - UCPClient ucpClient(host, port, username, password); - byte target[24]; - ucpClient.copyMiningTarget(target); - sprintf(outputBuffer, "Using Device: %d\n\n", deviceToUse); - cout << outputBuffer << endl; - Log::info(outputBuffer); - int version, ret; - ret = cudaDriverGetVersion(&version); - if (ret != cudaSuccess) - { - sprintf(outputBuffer, "Error when getting CUDA driver version: %d", ret); - cout << outputBuffer << endl; - Log::error(outputBuffer); - promptExit(-1); - } - int runtimeVersion; - ret = cudaRuntimeGetVersion(&runtimeVersion); - if (ret != cudaSuccess) - { - sprintf(outputBuffer, "Error when getting CUDA runtime version: %d", ret); - cout << outputBuffer << endl; - Log::error(outputBuffer); - promptExit(-1); + active_gpus = cuda_num_devices(); + if (active_gpus == 0) { + printf("No CUDA devices found! terminating.\n"); + exit(1); } - - - int deviceCount; - ret = cudaGetDeviceCount(&deviceCount); - if (ret != cudaSuccess) - { - sprintf(outputBuffer, "Error when getting CUDA device count: %d", ret); - cout << outputBuffer << endl; - Log::error(outputBuffer); - promptExit(-1); + for (int i = 0; i < MAX_GPUS; i++) { + device_map[i] = i % active_gpus; + device_name[i] = NULL; } + cuda_devicenames(); - cudaDeviceProp deviceProp; - -#if NVML - char driver[NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE]; - nvmlSystemGetDriverVersion(driver, NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE); -#else - char driver[] = "???.?? (NVML NOT ENABLED)"; -#endif - sprintf(outputBuffer, "CUDA Version: %.1f", ((float)version / 1000)); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, "CUDA Runtime Version: %d", runtimeVersion); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, "NVidia Driver Version: %s", driver); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, "CUDA Devices: %d", deviceCount); - cout << outputBuffer << endl << endl; - Log::info(outputBuffer); - - string selectedDeviceName; - // Print out information about all available CUDA devices on system - for (int count = 0; count < deviceCount; count++) - { - ret = cudaGetDeviceProperties(&deviceProp, count); - if (ret != cudaSuccess) - { - sprintf(outputBuffer, "An error occurred while getting the CUDA device properties: %d", ret); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - } - - if (count == deviceToUse) { - selectedDeviceName = deviceProp.name; - } - - sprintf(outputBuffer, "Device #%d (%s):", count, deviceProp.name); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Clock Rate: %d MHz", (deviceProp.clockRate / 1024)); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Is Integrated: %s", (deviceProp.integrated == 0 ? "false" : "true")); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Compute Capability: %d.%d", deviceProp.major, deviceProp.minor); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Kernel Concurrency: %d", deviceProp.concurrentKernels); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Max Grid Size: %d x %d x %d", deviceProp.maxGridSize[0], deviceProp.maxGridSize[1], deviceProp.maxGridSize[2]); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Max Threads per Block: %d", deviceProp.maxThreadsPerBlock); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Registers per Block: %d", deviceProp.regsPerBlock); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Registers per SM: %d", deviceProp.regsPerMultiprocessor); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Processor Count: %d", deviceProp.multiProcessorCount); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Shared Memory/Block: %zd", deviceProp.sharedMemPerBlock); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Shared Memory/Proc: %zd", deviceProp.sharedMemPerMultiprocessor); - cout << outputBuffer << endl; - Log::info(outputBuffer); - sprintf(outputBuffer, " Warp Size: %d", deviceProp.warpSize); +/* for (int i = 0; i < opt_n_threads; i++) { + cudaSetDevice(device_map[i]); + cudaDeviceReset(); + cudaSetDeviceFlags(cudaDeviceScheduleBlockingSync); + cudaFuncSetCacheConfig(vblakeHasher, cudaFuncCachePreferL1); + cudaError_t e = cudaGetLastError(); + sprintf(outputBuffer, "Last error: %s\n", cudaGetErrorString(e)); cout << outputBuffer << endl; Log::info(outputBuffer); - } - sprintf(outputBuffer, "Mining on device #%d...", deviceToUse); - cout << outputBuffer << endl; - Log::info(outputBuffer); - - ret = cudaSetDevice(deviceToUse); - if (ret != cudaSuccess) - { - sprintf(outputBuffer, "CUDA encountered an error while setting the device to %d:%d", deviceToUse, ret); - cerr << outputBuffer << endl; - Log::error(outputBuffer); } +*/ - cudaDeviceReset(); - - // Don't have GPU busy-wait on GPU - ret = cudaSetDeviceFlags(cudaDeviceScheduleBlockingSync); - - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Last error: %s\n", cudaGetErrorString(e)); - cout << outputBuffer << endl; - Log::info(outputBuffer); - - // Run initialization of device before beginning timer - uint64_t* header = getWork(ucpClient, (uint32_t)std::time(0)); - - unsigned long long startTime = std::time(0); - uint32_t nonceResult[1] = { 0 }; - uint64_t hashStart[1] = { 0 }; - - unsigned long long hashes = 0; - cudaError_t cudaStatus; - - uint32_t count = 0; - - int numLines = 0; - - // Mining loop - while (true) { - vprintf("top of mining loop\n"); - count++; - long timestamp = (long)std::time(0); - delete[] header; - vprintf("Getting work...\n"); - header = getWork(ucpClient, timestamp); - vprintf("Getting job id...\n"); - int jobId = ucpClient.getJobId(); - count++; - vprintf("Running kernel...\n"); - cudaStatus = grindNonces(nonceResult, hashStart, header); - vprintf("Kernel finished...\n"); - if (cudaStatus != cudaSuccess) { - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Error from running grindNonces: %s\nThis often occurs when a GPU overheats, has an unstable overclock, or has too aggressive launch parameters\nfor the vBlake kernel.\nYou can try using less aggressive settings, like:\n-tpb 256 -bs 256\nAnd try increasing these numbers until you hit instability issues again.", cudaGetErrorString(e)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - promptExit(-1); - } - - unsigned long long totalTime = std::time(0) - startTime; - hashes += (blocksize * threadsPerBlock * WORK_PER_THREAD); - - double hashSpeed = (double)hashes; - hashSpeed /= (totalTime * 1024 * 1024); - - if (count % 10 == 0) { - int validShares = ucpClient.getValidShares(); - int invalidShares = ucpClient.getInvalidShares(); - int totalAccountedForShares = invalidShares + validShares; - int totalSubmittedShares = ucpClient.getSentShares(); - int unaccountedForShares = totalSubmittedShares - totalAccountedForShares; - - double percentage = ((double)validShares) / totalAccountedForShares; - percentage *= 100; - // printf("[GPU #%d (%s)] : %f MH/second valid shares: %d/%d/%d (%.3f%%)\n", deviceToUse, selectedDeviceName.c_str(), hashSpeed, validShares, totalAccountedForShares, totalSubmittedShares, percentage); - - printf("[GPU #%d (%s)] : %0.2f MH/s shares: %d/%d/%d (%.3f%%)\n", deviceToUse, selectedDeviceName.c_str(), hashSpeed, validShares, totalAccountedForShares, totalSubmittedShares, percentage); - } - - if (nonceResult[0] != 0x01000000 && nonceResult[0] != 0) { - uint32_t nonce = *nonceResult; - nonce = (((nonce & 0xFF000000) >> 24) | ((nonce & 0x00FF0000) >> 8) | ((nonce & 0x0000FF00) << 8) | ((nonce & 0x000000FF) << 24)); - - ucpClient.submitWork(jobId, timestamp, nonce); - - nonceResult[0] = 0; - - char line[100]; - - // Hash coming from GPU is reversed - uint64_t hashFlipped = 0; - hashFlipped |= (hashStart[0] & 0x00000000000000FF) << 56; - hashFlipped |= (hashStart[0] & 0x000000000000FF00) << 40; - hashFlipped |= (hashStart[0] & 0x0000000000FF0000) << 24; - hashFlipped |= (hashStart[0] & 0x00000000FF000000) << 8; - hashFlipped |= (hashStart[0] & 0x000000FF00000000) >> 8; - hashFlipped |= (hashStart[0] & 0x0000FF0000000000) >> 24; - hashFlipped |= (hashStart[0] & 0x00FF000000000000) >> 40; - hashFlipped |= (hashStart[0] & 0xFF00000000000000) >> 56; - -#if CPU_SHARES - sprintf(line, "\t Share Found @ 2^24! {%#018llx} [nonce: %#08lx]", hashFlipped, nonce); -#else - sprintf(line, "\t Share Found @ 2^32! {%#018llx} [nonce: %#08lx]", hashFlipped, nonce); -#endif - - cout << line << endl; - vprintf("Logging\n"); - Log::info(line); - vprintf("Done logging\n"); - vprintf("Made line\n"); - - numLines++; + pthread_t tids[MAX_GPUS]; + struct mining_attr m_args[MAX_GPUS]; - // Uncomment these lines to get access to this data for display purposes - /* - long long extraNonce = ucpClient.getStartExtraNonce(); - int jobId = ucpClient.getJobId(); - int encodedDifficulty = ucpClient.getEncodedDifficulty(); - string previousBlockHashHex = ucpClient.getPreviousBlockHash(); - string merkleRoot = ucpClient.getMerkleRoot(); - */ + for (int i = 0; i < opt_n_threads; i++) { + m_args[i].host = host; + m_args[i].port = port; + m_args[i].username = username; + m_args[i].password = password; + m_args[i].dev_id = device_map[i]; - } - vprintf("About to restart loop...\n"); + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_create(&tids[i], &attr, miner_thread, &m_args[i]); } - printf("Resetting device...\n"); - cudaStatus = cudaDeviceReset(); - if (cudaStatus != cudaSuccess) { - fprintf(stderr, "cudaDeviceReset failed!"); - return 1; - } - printf("Done resetting device...\n"); + pthread_join(tids[0], NULL); - getchar(); - return 0; } - uint32_t lastNonceStart = 0; // Grind Through vBlake nonces with the provided header, setting the resultant nonce and associated hash start if a high-difficulty solution is found -cudaError_t grindNonces(uint32_t *nonceResult, uint64_t *hashStart, const uint64_t *header) +void grindNonces(uint32_t startnonce, uint32_t *nonceResult, uint64_t *hashStart, const uint64_t *header, int dev_id) { - // Device memory - uint32_t *dev_nonceStart = 0; - uint64_t *dev_header = 0; - uint32_t *dev_nonceResult = 0; - uint64_t *dev_hashStart = 0; - - // Ensure that nonces don't overlap previous work - uint32_t nonceStart = (uint64_t)lastNonceStart + (WORK_PER_THREAD * blocksize * threadsPerBlock); - lastNonceStart = nonceStart; - - cudaError_t cudaStatus; - // Select GPU to run on - cudaStatus = cudaSetDevice(deviceToUse); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "cudaSetDevice failed! Do you have a CUDA-capable GPU installed?"); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Cuda Error: %s\n", cudaGetErrorString(e)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; - } - + if (!init[dev_id]) + { + CUDA_SAFE_CALL(cudaSetDevice(dev_id)); + cudaDeviceReset(); + cudaSetDeviceFlags(cudaDeviceScheduleBlockingSync); + cudaFuncSetCacheConfig(vblakeHasher, cudaFuncCachePreferL1); // Allocate GPU buffers for nonce result and header - cudaStatus = cudaMalloc((void**)&dev_nonceStart, 1 * sizeof(uint32_t)); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "cudaMalloc failed!"); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Cuda Error: %s\n", cudaGetErrorString(e)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; + veri_init(dev_id); + init[dev_id] = 1; } - + // Copy starting nonce to GPU - cudaStatus = cudaMemcpy(dev_nonceStart, &nonceStart, sizeof(uint32_t), cudaMemcpyHostToDevice); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "cudaMemcpy failed!"); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Cuda Error: %s\n", cudaGetErrorString(e)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; - } - - // Allocate GPU buffers for nonce result and header. - cudaStatus = cudaMalloc((void**)&dev_nonceResult, 1 * sizeof(uint32_t)); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "cudaMalloc failed!"); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Cuda Error: %s\n", cudaGetErrorString(e)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; - } - - // Allocate GPU buffers for nonce result and header. - cudaStatus = cudaMalloc((void**)&dev_hashStart, 1 * sizeof(uint64_t)); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "cudaMalloc failed!"); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Cuda Error: %s\n", cudaGetErrorString(e)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; - } - - cudaStatus = cudaMalloc((void**)&dev_header, 8 * sizeof(uint64_t)); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "cudaMalloc failed!"); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Cuda Error: %s\n", cudaGetErrorString(e)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; - } - - // Copy input vectors from host memory to GPU buffers. - cudaStatus = cudaMemcpy(dev_header, header, 8 * sizeof(uint64_t), cudaMemcpyHostToDevice); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "cudaMalloc failed!"); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Cuda Error: %s\n", cudaGetErrorString(e)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; - } - - cudaMemcpyToSymbol(c_512, cpu_u512, sizeof(cpu_u512), 0, cudaMemcpyHostToDevice); - cudaMemcpyToSymbol(c_vblake, cpu_vBlake_iv, sizeof(cpu_vBlake_iv), 0, cudaMemcpyHostToDevice); + + veri_setBlock((void*)header); + CUDA_SAFE_CALL(cudaMemcpyToSymbol(c_512, cpu_u512, sizeof(cpu_u512), 0, cudaMemcpyHostToDevice)); + CUDA_SAFE_CALL(cudaMemcpyToSymbol(c_vblake, cpu_vBlake_iv, sizeof(cpu_vBlake_iv), 0, cudaMemcpyHostToDevice)); + cudaMemset(d_nonces[dev_id], 0x00, 1 * sizeof(uint32_t)); + cudaMemset(dev_nonceStart[dev_id], 0x00, 1 * sizeof(uint64_t)); // Launch a kernel on the GPU with one thread for each element. - vblakeHasher << < blocksize, threadsPerBlock >> >(dev_nonceStart, dev_nonceResult, dev_hashStart, dev_header); - - // Check for any errors launching the kernel - cudaStatus = cudaGetLastError(); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "grindNonces launch failed: %s\n", cudaGetErrorString(cudaStatus)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; - } - - // cudaDeviceSynchronize waits for the kernel to finish, and returns - // any errors encountered during the launch. - cudaStatus = cudaDeviceSynchronize(); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "cudaDeviceSynchronize returned error code %d after launching grindNonces!\n", cudaStatus); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; - } - - // Copy output vector from GPU buffer to host memory. - cudaStatus = cudaMemcpy(nonceResult, dev_nonceResult, 1 * sizeof(uint32_t), cudaMemcpyDeviceToHost); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "cudaMemcpy failed!"); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Cuda Error: %s\n", cudaGetErrorString(e)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; - } - - - // Copy output vector from GPU buffer to host memory. - cudaStatus = cudaMemcpy(hashStart, dev_hashStart, 1 * sizeof(uint64_t), cudaMemcpyDeviceToHost); - if (cudaStatus != cudaSuccess) { - sprintf(outputBuffer, "cudaMemcpy failed!"); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - cudaError_t e = cudaGetLastError(); - sprintf(outputBuffer, "Cuda Error: %s\n", cudaGetErrorString(e)); - cerr << outputBuffer << endl; - Log::error(outputBuffer); - goto Error; - } - -Error: - cudaFree(dev_nonceStart); - cudaFree(dev_header); - cudaFree(dev_nonceResult); - cudaFree(dev_hashStart); - return cudaStatus; -} + vblakeHasher << < blocksize, threadsPerBlock >> >(startnonce, d_nonces[dev_id], dev_nonceStart[dev_id]); + cudaThreadSynchronize(); + cudaMemcpy(nonceResult, d_nonces[dev_id], 1 * sizeof(uint32_t), cudaMemcpyDeviceToHost); + cudaMemcpy(hashStart, dev_nonceStart[dev_id], 1 * sizeof(uint64_t), cudaMemcpyDeviceToHost); +}; diff --git a/libs/pthreadVC2.lib b/libs/pthreadVC2.lib new file mode 100644 index 0000000000000000000000000000000000000000..3e26e4a066c3eeca1a1de8a78bf9c089b1750407 GIT binary patch literal 166580 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 199309 +#undef PTW32_LEVEL +#define PTW32_LEVEL 1 +/* Include 1b, 1c and 1d */ +#endif + +#if defined(INCLUDE_NP) +#undef PTW32_LEVEL +#define PTW32_LEVEL 2 +/* Include Non-Portable extensions */ +#endif + +#define PTW32_LEVEL_MAX 3 + +#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_LEVEL) +#define PTW32_LEVEL PTW32_LEVEL_MAX +/* Include everything */ +#endif + +#if defined(_UWIN) +# define HAVE_STRUCT_TIMESPEC 1 +# define HAVE_SIGNAL_H 1 +# undef HAVE_PTW32_CONFIG_H +# pragma comment(lib, "pthread") +#endif + +/* + * ------------------------------------------------------------- + * + * + * Module: pthread.h + * + * Purpose: + * Provides an implementation of PThreads based upon the + * standard: + * + * POSIX 1003.1-2001 + * and + * The Single Unix Specification version 3 + * + * (these two are equivalent) + * + * in order to enhance code portability between Windows, + * various commercial Unix implementations, and Linux. + * + * See the ANNOUNCE file for a full list of conforming + * routines and defined constants, and a list of missing + * routines and constants not defined in this implementation. + * + * Authors: + * There have been many contributors to this library. + * The initial implementation was contributed by + * John Bossom, and several others have provided major + * sections or revisions of parts of the implementation. + * Often significant effort has been contributed to + * find and fix important bugs and other problems to + * improve the reliability of the library, which sometimes + * is not reflected in the amount of code which changed as + * result. + * As much as possible, the contributors are acknowledged + * in the ChangeLog file in the source code distribution + * where their changes are noted in detail. + * + * Contributors are listed in the CONTRIBUTORS file. + * + * As usual, all bouquets go to the contributors, and all + * brickbats go to the project maintainer. + * + * Maintainer: + * The code base for this project is coordinated and + * eventually pre-tested, packaged, and made available by + * + * Ross Johnson + * + * QA Testers: + * Ultimately, the library is tested in the real world by + * a host of competent and demanding scientists and + * engineers who report bugs and/or provide solutions + * which are then fixed or incorporated into subsequent + * versions of the library. Each time a bug is fixed, a + * test case is written to prove the fix and ensure + * that later changes to the code don't reintroduce the + * same error. The number of test cases is slowly growing + * and therefore so is the code reliability. + * + * Compliance: + * See the file ANNOUNCE for the list of implemented + * and not-implemented routines and defined options. + * Of course, these are all defined is this file as well. + * + * Web site: + * The source code and other information about this library + * are available from + * + * http://sources.redhat.com/pthreads-win32/ + * + * ------------------------------------------------------------- + */ + +/* Try to avoid including windows.h */ +#if (defined(__MINGW64__) || defined(__MINGW32__)) && defined(__cplusplus) +#define PTW32_INCLUDE_WINDOWS_H +#endif + +#if defined(PTW32_INCLUDE_WINDOWS_H) +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1300 || defined(__DMC__) +/* + * VC++6.0 or early compiler's header has no DWORD_PTR type. + */ +typedef unsigned long DWORD_PTR; +typedef unsigned long ULONG_PTR; +#endif +/* + * ----------------- + * autoconf switches + * ----------------- + */ + +#if defined(HAVE_PTW32_CONFIG_H) +#include "config.h" +#endif /* HAVE_PTW32_CONFIG_H */ + +#if !defined(NEED_FTIME) +#include +#else /* NEED_FTIME */ +/* use native WIN32 time API */ +#endif /* NEED_FTIME */ + +#if defined(HAVE_SIGNAL_H) +#include +#endif /* HAVE_SIGNAL_H */ + +#include + +/* + * Boolean values to make us independent of system includes. + */ +enum { + PTW32_FALSE = 0, + PTW32_TRUE = (! PTW32_FALSE) +}; + +/* + * This is a duplicate of what is in the autoconf config.h, + * which is only used when building the pthread-win32 libraries. + */ + +#if !defined(PTW32_CONFIG_H) +# if defined(WINCE) +# define NEED_ERRNO +# define NEED_SEM +# endif +# if defined(__MINGW64__) +# define HAVE_STRUCT_TIMESPEC +# define HAVE_MODE_T +# elif defined(_UWIN) || defined(__MINGW32__) +# define HAVE_MODE_T +# endif +#endif + +/* + * + */ + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX +#if defined(NEED_ERRNO) +#include "need_errno.h" +#else +#include +#endif +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Several systems don't define some error numbers. + */ +#if !defined(ENOTSUP) +# define ENOTSUP 48 /* This is the value in Solaris. */ +#endif + +#if !defined(ETIMEDOUT) +# define ETIMEDOUT 10060 /* Same as WSAETIMEDOUT */ +#endif + +#if !defined(ENOSYS) +# define ENOSYS 140 /* Semi-arbitrary value */ +#endif + +#if !defined(EDEADLK) +# if defined(EDEADLOCK) +# define EDEADLK EDEADLOCK +# else +# define EDEADLK 36 /* This is the value in MSVC. */ +# endif +#endif + +/* POSIX 2008 - related to robust mutexes */ +#if !defined(EOWNERDEAD) +# define EOWNERDEAD 43 +#endif +#if !defined(ENOTRECOVERABLE) +# define ENOTRECOVERABLE 44 +#endif + +#include "./pthreads/sched.h" + +/* + * To avoid including windows.h we define only those things that we + * actually need from it. + */ +#if !defined(PTW32_INCLUDE_WINDOWS_H) +#if !defined(HANDLE) +# define PTW32__HANDLE_DEF +# define HANDLE void * +#endif +#if !defined(DWORD) +# define PTW32__DWORD_DEF +# define DWORD unsigned long +#endif +#endif + +#if !defined(HAVE_STRUCT_TIMESPEC) +#define HAVE_STRUCT_TIMESPEC +#if !defined(_TIMESPEC_DEFINED) +#define _TIMESPEC_DEFINED +struct timespec { + time_t tv_sec; + long tv_nsec; +}; +#endif /* _TIMESPEC_DEFINED */ +#endif /* HAVE_STRUCT_TIMESPEC */ + +#if !defined(SIG_BLOCK) +#define SIG_BLOCK 0 +#endif /* SIG_BLOCK */ + +#if !defined(SIG_UNBLOCK) +#define SIG_UNBLOCK 1 +#endif /* SIG_UNBLOCK */ + +#if !defined(SIG_SETMASK) +#define SIG_SETMASK 2 +#endif /* SIG_SETMASK */ + +#if defined(__cplusplus) +extern "C" +{ +#endif /* __cplusplus */ + +/* + * ------------------------------------------------------------- + * + * POSIX 1003.1-2001 Options + * ========================= + * + * Options are normally set in , which is not provided + * with pthreads-win32. + * + * For conformance with the Single Unix Specification (version 3), all of the + * options below are defined, and have a value of either -1 (not supported) + * or 200112L (supported). + * + * These options can neither be left undefined nor have a value of 0, because + * either indicates that sysconf(), which is not implemented, may be used at + * runtime to check the status of the option. + * + * _POSIX_THREADS (== 200112L) + * If == 200112L, you can use threads + * + * _POSIX_THREAD_ATTR_STACKSIZE (== 200112L) + * If == 200112L, you can control the size of a thread's + * stack + * pthread_attr_getstacksize + * pthread_attr_setstacksize + * + * _POSIX_THREAD_ATTR_STACKADDR (== -1) + * If == 200112L, you can allocate and control a thread's + * stack. If not supported, the following functions + * will return ENOSYS, indicating they are not + * supported: + * pthread_attr_getstackaddr + * pthread_attr_setstackaddr + * + * _POSIX_THREAD_PRIORITY_SCHEDULING (== -1) + * If == 200112L, you can use realtime scheduling. + * This option indicates that the behaviour of some + * implemented functions conforms to the additional TPS + * requirements in the standard. E.g. rwlocks favour + * writers over readers when threads have equal priority. + * + * _POSIX_THREAD_PRIO_INHERIT (== -1) + * If == 200112L, you can create priority inheritance + * mutexes. + * pthread_mutexattr_getprotocol + + * pthread_mutexattr_setprotocol + + * + * _POSIX_THREAD_PRIO_PROTECT (== -1) + * If == 200112L, you can create priority ceiling mutexes + * Indicates the availability of: + * pthread_mutex_getprioceiling + * pthread_mutex_setprioceiling + * pthread_mutexattr_getprioceiling + * pthread_mutexattr_getprotocol + + * pthread_mutexattr_setprioceiling + * pthread_mutexattr_setprotocol + + * + * _POSIX_THREAD_PROCESS_SHARED (== -1) + * If set, you can create mutexes and condition + * variables that can be shared with another + * process.If set, indicates the availability + * of: + * pthread_mutexattr_getpshared + * pthread_mutexattr_setpshared + * pthread_condattr_getpshared + * pthread_condattr_setpshared + * + * _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L) + * If == 200112L you can use the special *_r library + * functions that provide thread-safe behaviour + * + * _POSIX_READER_WRITER_LOCKS (== 200112L) + * If == 200112L, you can use read/write locks + * + * _POSIX_SPIN_LOCKS (== 200112L) + * If == 200112L, you can use spin locks + * + * _POSIX_BARRIERS (== 200112L) + * If == 200112L, you can use barriers + * + * + These functions provide both 'inherit' and/or + * 'protect' protocol, based upon these macro + * settings. + * + * ------------------------------------------------------------- + */ + +/* + * POSIX Options + */ +#undef _POSIX_THREADS +#define _POSIX_THREADS 200809L + +#undef _POSIX_READER_WRITER_LOCKS +#define _POSIX_READER_WRITER_LOCKS 200809L + +#undef _POSIX_SPIN_LOCKS +#define _POSIX_SPIN_LOCKS 200809L + +#undef _POSIX_BARRIERS +#define _POSIX_BARRIERS 200809L + +#undef _POSIX_THREAD_SAFE_FUNCTIONS +#define _POSIX_THREAD_SAFE_FUNCTIONS 200809L + +#undef _POSIX_THREAD_ATTR_STACKSIZE +#define _POSIX_THREAD_ATTR_STACKSIZE 200809L + +/* + * The following options are not supported + */ +#undef _POSIX_THREAD_ATTR_STACKADDR +#define _POSIX_THREAD_ATTR_STACKADDR -1 + +#undef _POSIX_THREAD_PRIO_INHERIT +#define _POSIX_THREAD_PRIO_INHERIT -1 + +#undef _POSIX_THREAD_PRIO_PROTECT +#define _POSIX_THREAD_PRIO_PROTECT -1 + +/* TPS is not fully supported. */ +#undef _POSIX_THREAD_PRIORITY_SCHEDULING +#define _POSIX_THREAD_PRIORITY_SCHEDULING -1 + +#undef _POSIX_THREAD_PROCESS_SHARED +#define _POSIX_THREAD_PROCESS_SHARED -1 + + +/* + * POSIX 1003.1-2001 Limits + * =========================== + * + * These limits are normally set in , which is not provided with + * pthreads-win32. + * + * PTHREAD_DESTRUCTOR_ITERATIONS + * Maximum number of attempts to destroy + * a thread's thread-specific data on + * termination (must be at least 4) + * + * PTHREAD_KEYS_MAX + * Maximum number of thread-specific data keys + * available per process (must be at least 128) + * + * PTHREAD_STACK_MIN + * Minimum supported stack size for a thread + * + * PTHREAD_THREADS_MAX + * Maximum number of threads supported per + * process (must be at least 64). + * + * SEM_NSEMS_MAX + * The maximum number of semaphores a process can have. + * (must be at least 256) + * + * SEM_VALUE_MAX + * The maximum value a semaphore can have. + * (must be at least 32767) + * + */ +#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 + +#undef PTHREAD_DESTRUCTOR_ITERATIONS +#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS + +#undef _POSIX_THREAD_KEYS_MAX +#define _POSIX_THREAD_KEYS_MAX 128 + +#undef PTHREAD_KEYS_MAX +#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX + +#undef PTHREAD_STACK_MIN +#define PTHREAD_STACK_MIN 0 + +#undef _POSIX_THREAD_THREADS_MAX +#define _POSIX_THREAD_THREADS_MAX 64 + + /* Arbitrary value */ +#undef PTHREAD_THREADS_MAX +#define PTHREAD_THREADS_MAX 2019 + +#undef _POSIX_SEM_NSEMS_MAX +#define _POSIX_SEM_NSEMS_MAX 256 + + /* Arbitrary value */ +#undef SEM_NSEMS_MAX +#define SEM_NSEMS_MAX 1024 + +#undef _POSIX_SEM_VALUE_MAX +#define _POSIX_SEM_VALUE_MAX 32767 + +#undef SEM_VALUE_MAX +#define SEM_VALUE_MAX INT_MAX + + +#if defined(__GNUC__) && !defined(__declspec) +# error Please upgrade your GNU compiler to one that supports __declspec. +#endif + +/* + * When building the library, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the library, + * do NOT define PTW32_BUILD, and then the variables/functions will + * be imported correctly. + */ +#if !defined(PTW32_STATIC_LIB) +# if defined(PTW32_BUILD) +# define PTW32_DLLPORT __declspec (dllexport) +# else +# define PTW32_DLLPORT __declspec (dllimport) +# endif +#else +# define PTW32_DLLPORT +#endif + +/* + * The Open Watcom C/C++ compiler uses a non-standard calling convention + * that passes function args in registers unless __cdecl is explicitly specified + * in exposed function prototypes. + * + * We force all calls to cdecl even though this could slow Watcom code down + * slightly. If you know that the Watcom compiler will be used to build both + * the DLL and application, then you can probably define this as a null string. + * Remember that pthread.h (this file) is used for both the DLL and application builds. + */ +#define PTW32_CDECL __cdecl + +#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX +# include +#else +/* + * Generic handle type - intended to extend uniqueness beyond + * that available with a simple pointer. It should scale for either + * IA-32 or IA-64. + */ +typedef struct { + void * p; /* Pointer to actual object */ + unsigned int x; /* Extra information - reuse count etc */ +} ptw32_handle_t; + +typedef ptw32_handle_t pthread_t; +typedef struct pthread_attr_t_ * pthread_attr_t; +typedef struct pthread_once_t_ pthread_once_t; +typedef struct pthread_key_t_ * pthread_key_t; +typedef struct pthread_mutex_t_ * pthread_mutex_t; +typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t; +typedef struct pthread_cond_t_ * pthread_cond_t; +typedef struct pthread_condattr_t_ * pthread_condattr_t; +#endif +typedef struct pthread_rwlock_t_ * pthread_rwlock_t; +typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t; +typedef struct pthread_spinlock_t_ * pthread_spinlock_t; +typedef struct pthread_barrier_t_ * pthread_barrier_t; +typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t; + +/* + * ==================== + * ==================== + * POSIX Threads + * ==================== + * ==================== + */ + +enum { +/* + * pthread_attr_{get,set}detachstate + */ + PTHREAD_CREATE_JOINABLE = 0, /* Default */ + PTHREAD_CREATE_DETACHED = 1, + +/* + * pthread_attr_{get,set}inheritsched + */ + PTHREAD_INHERIT_SCHED = 0, + PTHREAD_EXPLICIT_SCHED = 1, /* Default */ + +/* + * pthread_{get,set}scope + */ + PTHREAD_SCOPE_PROCESS = 0, + PTHREAD_SCOPE_SYSTEM = 1, /* Default */ + +/* + * pthread_setcancelstate paramters + */ + PTHREAD_CANCEL_ENABLE = 0, /* Default */ + PTHREAD_CANCEL_DISABLE = 1, + +/* + * pthread_setcanceltype parameters + */ + PTHREAD_CANCEL_ASYNCHRONOUS = 0, + PTHREAD_CANCEL_DEFERRED = 1, /* Default */ + +/* + * pthread_mutexattr_{get,set}pshared + * pthread_condattr_{get,set}pshared + */ + PTHREAD_PROCESS_PRIVATE = 0, + PTHREAD_PROCESS_SHARED = 1, + +/* + * pthread_mutexattr_{get,set}robust + */ + PTHREAD_MUTEX_STALLED = 0, /* Default */ + PTHREAD_MUTEX_ROBUST = 1, + +/* + * pthread_barrier_wait + */ + PTHREAD_BARRIER_SERIAL_THREAD = -1 +}; + +/* + * ==================== + * ==================== + * Cancelation + * ==================== + * ==================== + */ +#define PTHREAD_CANCELED ((void *)(size_t) -1) + + +/* + * ==================== + * ==================== + * Once Key + * ==================== + * ==================== + */ +#define PTHREAD_ONCE_INIT { PTW32_FALSE, 0, 0, 0} + +struct pthread_once_t_ +{ + int done; /* indicates if user function has been executed */ + void * lock; + int reserved1; + int reserved2; +}; + + +/* + * ==================== + * ==================== + * Object initialisers + * ==================== + * ==================== + */ +#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -1) +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -2) +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -3) + +/* + * Compatibility with LinuxThreads + */ +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER + +#define PTHREAD_COND_INITIALIZER ((pthread_cond_t)(size_t) -1) + +#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t)(size_t) -1) + +#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t)(size_t) -1) + + +/* + * Mutex types. + */ +enum +{ + /* Compatibility with LinuxThreads */ + PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP, + /* For compatibility with POSIX */ + PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL +}; + + +typedef struct ptw32_cleanup_t ptw32_cleanup_t; + +#if defined(_MSC_VER) +/* Disable MSVC 'anachronism used' warning */ +#pragma warning( disable : 4229 ) +#endif + +typedef void (* PTW32_CDECL ptw32_cleanup_callback_t)(void *); + +#if defined(_MSC_VER) +#pragma warning( default : 4229 ) +#endif + +struct ptw32_cleanup_t +{ + ptw32_cleanup_callback_t routine; + void *arg; + struct ptw32_cleanup_t *prev; +}; + +#if defined(__CLEANUP_SEH) + /* + * WIN32 SEH version of cancel cleanup. + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + ptw32_cleanup_t _cleanup; \ + \ + _cleanup.routine = (ptw32_cleanup_callback_t)(_rout); \ + _cleanup.arg = (_arg); \ + __try \ + { \ + +#define pthread_cleanup_pop( _execute ) \ + } \ + __finally \ + { \ + if( _execute || AbnormalTermination()) \ + { \ + (*(_cleanup.routine))( _cleanup.arg ); \ + } \ + } \ + } + +#else /* __CLEANUP_SEH */ + +#if defined(__CLEANUP_C) + + /* + * C implementation of PThreads cancel cleanup + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + ptw32_cleanup_t _cleanup; \ + \ + ptw32_push_cleanup( &_cleanup, (ptw32_cleanup_callback_t) (_rout), (_arg) ); \ + +#define pthread_cleanup_pop( _execute ) \ + (void) ptw32_pop_cleanup( _execute ); \ + } + +#else /* __CLEANUP_C */ + +#if defined(__CLEANUP_CXX) + + /* + * C++ version of cancel cleanup. + * - John E. Bossom. + */ + + class PThreadCleanup { + /* + * PThreadCleanup + * + * Purpose + * This class is a C++ helper class that is + * used to implement pthread_cleanup_push/ + * pthread_cleanup_pop. + * The destructor of this class automatically + * pops the pushed cleanup routine regardless + * of how the code exits the scope + * (i.e. such as by an exception) + */ + ptw32_cleanup_callback_t cleanUpRout; + void * obj; + int executeIt; + + public: + PThreadCleanup() : + cleanUpRout( 0 ), + obj( 0 ), + executeIt( 0 ) + /* + * No cleanup performed + */ + { + } + + PThreadCleanup( + ptw32_cleanup_callback_t routine, + void * arg ) : + cleanUpRout( routine ), + obj( arg ), + executeIt( 1 ) + /* + * Registers a cleanup routine for 'arg' + */ + { + } + + ~PThreadCleanup() + { + if ( executeIt && ((void *) cleanUpRout != (void *) 0) ) + { + (void) (*cleanUpRout)( obj ); + } + } + + void execute( int exec ) + { + executeIt = exec; + } + }; + + /* + * C++ implementation of PThreads cancel cleanup; + * This implementation takes advantage of a helper + * class who's destructor automatically calls the + * cleanup routine if we exit our scope weirdly + */ +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + PThreadCleanup cleanup((ptw32_cleanup_callback_t)(_rout), \ + (void *) (_arg) ); + +#define pthread_cleanup_pop( _execute ) \ + cleanup.execute( _execute ); \ + } + +#else + +#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined. + +#endif /* __CLEANUP_CXX */ + +#endif /* __CLEANUP_C */ + +#endif /* __CLEANUP_SEH */ + +/* + * =============== + * =============== + * Methods + * =============== + * =============== + */ + +/* + * PThread Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_attr_init (pthread_attr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_destroy (pthread_attr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getdetachstate (const pthread_attr_t * attr, + int *detachstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstackaddr (const pthread_attr_t * attr, + void **stackaddr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstacksize (const pthread_attr_t * attr, + size_t * stacksize); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setdetachstate (pthread_attr_t * attr, + int detachstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstackaddr (pthread_attr_t * attr, + void *stackaddr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstacksize (pthread_attr_t * attr, + size_t stacksize); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedparam (const pthread_attr_t *attr, + struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedparam (pthread_attr_t *attr, + const struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedpolicy (pthread_attr_t *, + int); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedpolicy (const pthread_attr_t *, + int *); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setinheritsched(pthread_attr_t * attr, + int inheritsched); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getinheritsched(const pthread_attr_t * attr, + int * inheritsched); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setscope (pthread_attr_t *, + int); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getscope (const pthread_attr_t *, + int *); + +/* + * PThread Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid, + const pthread_attr_t * attr, + void *(PTW32_CDECL *start) (void *), + void *arg); + +PTW32_DLLPORT int PTW32_CDECL pthread_detach (pthread_t tid); + +PTW32_DLLPORT int PTW32_CDECL pthread_equal (pthread_t t1, + pthread_t t2); + +PTW32_DLLPORT void PTW32_CDECL pthread_exit (void *value_ptr); + +PTW32_DLLPORT int PTW32_CDECL pthread_join (pthread_t thread, + void **value_ptr); + +PTW32_DLLPORT pthread_t PTW32_CDECL pthread_self (void); + +PTW32_DLLPORT int PTW32_CDECL pthread_cancel (pthread_t thread); + +PTW32_DLLPORT int PTW32_CDECL pthread_setcancelstate (int state, + int *oldstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_setcanceltype (int type, + int *oldtype); + +PTW32_DLLPORT void PTW32_CDECL pthread_testcancel (void); + +PTW32_DLLPORT int PTW32_CDECL pthread_once (pthread_once_t * once_control, + void (PTW32_CDECL *init_routine) (void)); + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX +PTW32_DLLPORT ptw32_cleanup_t * PTW32_CDECL ptw32_pop_cleanup (int execute); + +PTW32_DLLPORT void PTW32_CDECL ptw32_push_cleanup (ptw32_cleanup_t * cleanup, + ptw32_cleanup_callback_t routine, + void *arg); +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Thread Specific Data Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_key_create (pthread_key_t * key, + void (PTW32_CDECL *destructor) (void *)); + +PTW32_DLLPORT int PTW32_CDECL pthread_key_delete (pthread_key_t key); + +PTW32_DLLPORT int PTW32_CDECL pthread_setspecific (pthread_key_t key, + const void *value); + +PTW32_DLLPORT void * PTW32_CDECL pthread_getspecific (pthread_key_t key); + + +/* + * Mutex Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_init (pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_destroy (pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getpshared (const pthread_mutexattr_t + * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, + int pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_gettype (const pthread_mutexattr_t * attr, int *kind); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setrobust( + pthread_mutexattr_t *attr, + int robust); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getrobust( + const pthread_mutexattr_t * attr, + int * robust); + +/* + * Barrier Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_init (pthread_barrierattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_destroy (pthread_barrierattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_getpshared (const pthread_barrierattr_t + * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_setpshared (pthread_barrierattr_t * attr, + int pshared); + +/* + * Mutex Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_init (pthread_mutex_t * mutex, + const pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_destroy (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_lock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_timedlock(pthread_mutex_t * mutex, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_trylock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_unlock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_consistent (pthread_mutex_t * mutex); + +/* + * Spinlock Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_spin_init (pthread_spinlock_t * lock, int pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_destroy (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_lock (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_trylock (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_unlock (pthread_spinlock_t * lock); + +/* + * Barrier Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_init (pthread_barrier_t * barrier, + const pthread_barrierattr_t * attr, + unsigned int count); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_destroy (pthread_barrier_t * barrier); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_wait (pthread_barrier_t * barrier); + +/* + * Condition Variable Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_init (pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_destroy (pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_getpshared (const pthread_condattr_t * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_setpshared (pthread_condattr_t * attr, + int pshared); + +/* + * Condition Variable Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_cond_init (pthread_cond_t * cond, + const pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_destroy (pthread_cond_t * cond); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_wait (pthread_cond_t * cond, + pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_timedwait (pthread_cond_t * cond, + pthread_mutex_t * mutex, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_signal (pthread_cond_t * cond); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_broadcast (pthread_cond_t * cond); + +/* + * Scheduling + */ +PTW32_DLLPORT int PTW32_CDECL pthread_setschedparam (pthread_t thread, + int policy, + const struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_getschedparam (pthread_t thread, + int *policy, + struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_setconcurrency (int); + +PTW32_DLLPORT int PTW32_CDECL pthread_getconcurrency (void); + +/* + * Read-Write Lock Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_init(pthread_rwlock_t *lock, + const pthread_rwlockattr_t *attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_destroy(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_tryrdlock(pthread_rwlock_t *); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_trywrlock(pthread_rwlock_t *); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_rdlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedrdlock(pthread_rwlock_t *lock, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_wrlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedwrlock(pthread_rwlock_t *lock, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_unlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_init (pthread_rwlockattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr, + int pshared); + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 + +/* + * Signal Functions. Should be defined in but MSVC and MinGW32 + * already have signal.h that don't define these. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_kill(pthread_t thread, int sig); + +/* + * Non-portable functions + */ + +/* + * Compatibility with Linux. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr, + int kind); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr, + int *kind); + +/* + * Possibly supported by other POSIX threads implementations + */ +PTW32_DLLPORT int PTW32_CDECL pthread_delay_np (struct timespec * interval); +PTW32_DLLPORT int PTW32_CDECL pthread_num_processors_np(void); +PTW32_DLLPORT unsigned __int64 PTW32_CDECL pthread_getunique_np(pthread_t thread); + +/* + * Useful if an application wants to statically link + * the lib rather than load the DLL at run-time. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_attach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_detach_np(void); + +/* + * Features that are auto-detected at load/run time. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_win32_test_features_np(int); +enum ptw32_features { + PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE = 0x0001, /* System provides it. */ + PTW32_ALERTABLE_ASYNC_CANCEL = 0x0002 /* Can cancel blocked threads. */ +}; + +/* + * Register a system time change with the library. + * Causes the library to perform various functions + * in response to the change. Should be called whenever + * the application's top level window receives a + * WM_TIMECHANGE message. It can be passed directly to + * pthread_create() as a new thread if desired. + */ +PTW32_DLLPORT void * PTW32_CDECL pthread_timechange_handler_np(void *); + +#endif /*PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 */ + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX + +/* + * Returns the Win32 HANDLE for the POSIX thread. + */ +PTW32_DLLPORT HANDLE PTW32_CDECL pthread_getw32threadhandle_np(pthread_t thread); +/* + * Returns the win32 thread ID for POSIX thread. + */ +PTW32_DLLPORT DWORD PTW32_CDECL pthread_getw32threadid_np (pthread_t thread); + + +/* + * Protected Methods + * + * This function blocks until the given WIN32 handle + * is signaled or pthread_cancel had been called. + * This function allows the caller to hook into the + * PThreads cancel mechanism. It is implemented using + * + * WaitForMultipleObjects + * + * on 'waitHandle' and a manually reset WIN32 Event + * used to implement pthread_cancel. The 'timeout' + * argument to TimedWait is simply passed to + * WaitForMultipleObjects. + */ +PTW32_DLLPORT int PTW32_CDECL pthreadCancelableWait (HANDLE waitHandle); +PTW32_DLLPORT int PTW32_CDECL pthreadCancelableTimedWait (HANDLE waitHandle, + DWORD timeout); + +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Thread-Safe C Runtime Library Mappings. + */ +#if !defined(_UWIN) +# if defined(NEED_ERRNO) + PTW32_DLLPORT int * PTW32_CDECL _errno( void ); +# else +# if !defined(errno) +# if (defined(_MT) || defined(_DLL)) + __declspec(dllimport) extern int * __cdecl _errno(void); +# define errno (*_errno()) +# endif +# endif +# endif +#endif + +/* + * Some compiler environments don't define some things. + */ +#if defined(__BORLANDC__) +# define _ftime ftime +# define _timeb timeb +#endif + +#if defined(__cplusplus) + +/* + * Internal exceptions + */ +class ptw32_exception {}; +class ptw32_exception_cancel : public ptw32_exception {}; +class ptw32_exception_exit : public ptw32_exception {}; + +#endif + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX + +/* FIXME: This is only required if the library was built using SEH */ +/* + * Get internal SEH tag + */ +PTW32_DLLPORT DWORD PTW32_CDECL ptw32_get_exception_services_code(void); + +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +#if !defined(PTW32_BUILD) + +#if defined(__CLEANUP_SEH) + +/* + * Redefine the SEH __except keyword to ensure that applications + * propagate our internal exceptions up to the library's internal handlers. + */ +#define __except( E ) \ + __except( ( GetExceptionCode() == ptw32_get_exception_services_code() ) \ + ? EXCEPTION_CONTINUE_SEARCH : ( E ) ) + +#endif /* __CLEANUP_SEH */ + +#if defined(__CLEANUP_CXX) + +/* + * Redefine the C++ catch keyword to ensure that applications + * propagate our internal exceptions up to the library's internal handlers. + */ +#if defined(_MSC_VER) + /* + * WARNING: Replace any 'catch( ... )' with 'PtW32CatchAll' + * if you want Pthread-Win32 cancelation and pthread_exit to work. + */ + +#if !defined(PtW32NoCatchWarn) + +#pragma message("Specify \"/DPtW32NoCatchWarn\" compiler flag to skip this message.") +#pragma message("------------------------------------------------------------------") +#pragma message("When compiling applications with MSVC++ and C++ exception handling:") +#pragma message(" Replace any 'catch( ... )' in routines called from POSIX threads") +#pragma message(" with 'PtW32CatchAll' or 'CATCHALL' if you want POSIX thread") +#pragma message(" cancelation and pthread_exit to work. For example:") +#pragma message("") +#pragma message(" #if defined(PtW32CatchAll)") +#pragma message(" PtW32CatchAll") +#pragma message(" #else") +#pragma message(" catch(...)") +#pragma message(" #endif") +#pragma message(" {") +#pragma message(" /* Catchall block processing */") +#pragma message(" }") +#pragma message("------------------------------------------------------------------") + +#endif + +#define PtW32CatchAll \ + catch( ptw32_exception & ) { throw; } \ + catch( ... ) + +#else /* _MSC_VER */ + +#define catch( E ) \ + catch( ptw32_exception & ) { throw; } \ + catch( E ) + +#endif /* _MSC_VER */ + +#endif /* __CLEANUP_CXX */ + +#endif /* ! PTW32_BUILD */ + +#if defined(__cplusplus) +} /* End of extern "C" */ +#endif /* __cplusplus */ + +#if defined(PTW32__HANDLE_DEF) +# undef HANDLE +#endif +#if defined(PTW32__DWORD_DEF) +# undef DWORD +#endif + +#undef PTW32_LEVEL +#undef PTW32_LEVEL_MAX + +#endif /* ! RC_INVOKED */ + +#endif /* PTHREAD_H */ diff --git a/pthreads/sched.h b/pthreads/sched.h new file mode 100644 index 0000000..f36a97a --- /dev/null +++ b/pthreads/sched.h @@ -0,0 +1,183 @@ +/* + * Module: sched.h + * + * Purpose: + * Provides an implementation of POSIX realtime extensions + * as defined in + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(_SCHED_H) +#define _SCHED_H + +#undef PTW32_SCHED_LEVEL + +#if defined(_POSIX_SOURCE) +#define PTW32_SCHED_LEVEL 0 +/* Early POSIX */ +#endif + +#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309 +#undef PTW32_SCHED_LEVEL +#define PTW32_SCHED_LEVEL 1 +/* Include 1b, 1c and 1d */ +#endif + +#if defined(INCLUDE_NP) +#undef PTW32_SCHED_LEVEL +#define PTW32_SCHED_LEVEL 2 +/* Include Non-Portable extensions */ +#endif + +#define PTW32_SCHED_LEVEL_MAX 3 + +#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_SCHED_LEVEL) +#define PTW32_SCHED_LEVEL PTW32_SCHED_LEVEL_MAX +/* Include everything */ +#endif + + +#if defined(__GNUC__) && !defined(__declspec) +# error Please upgrade your GNU compiler to one that supports __declspec. +#endif + +/* + * When building the library, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the library, + * do NOT define PTW32_BUILD, and then the variables/functions will + * be imported correctly. + */ +#if !defined(PTW32_STATIC_LIB) +# if defined(PTW32_BUILD) +# define PTW32_DLLPORT __declspec (dllexport) +# else +# define PTW32_DLLPORT __declspec (dllimport) +# endif +#else +# define PTW32_DLLPORT +#endif + +/* + * This is a duplicate of what is in the autoconf config.h, + * which is only used when building the pthread-win32 libraries. + */ + +#if !defined(PTW32_CONFIG_H) +# if defined(WINCE) +# define NEED_ERRNO +# define NEED_SEM +# endif +# if defined(__MINGW64__) +# define HAVE_STRUCT_TIMESPEC +# define HAVE_MODE_T +# elif defined(_UWIN) || defined(__MINGW32__) +# define HAVE_MODE_T +# endif +#endif + +/* + * + */ + +#if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX +#if defined(NEED_ERRNO) +#include "need_errno.h" +#else +#include +#endif +#endif /* PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX */ + +#if (defined(__MINGW64__) || defined(__MINGW32__)) || defined(_UWIN) +# if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX +/* For pid_t */ +# include +/* Required by Unix 98 */ +# include +# else + typedef int pid_t; +# endif +#else + typedef int pid_t; +#endif + +/* Thread scheduling policies */ + +enum { + SCHED_OTHER = 0, + SCHED_FIFO, + SCHED_RR, + SCHED_MIN = SCHED_OTHER, + SCHED_MAX = SCHED_RR +}; + +struct sched_param { + int sched_priority; +}; + +#if defined(__cplusplus) +extern "C" +{ +#endif /* __cplusplus */ + +PTW32_DLLPORT int __cdecl sched_yield (void); + +PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy); + +PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy); + +PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy); + +PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid); + +/* + * Note that this macro returns ENOTSUP rather than + * ENOSYS as might be expected. However, returning ENOSYS + * should mean that sched_get_priority_{min,max} are + * not implemented as well as sched_rr_get_interval. + * This is not the case, since we just don't support + * round-robin scheduling. Therefore I have chosen to + * return the same value as sched_setscheduler when + * SCHED_RR is passed to it. + */ +#define sched_rr_get_interval(_pid, _interval) \ + ( errno = ENOTSUP, (int) -1 ) + + +#if defined(__cplusplus) +} /* End of extern "C" */ +#endif /* __cplusplus */ + +#undef PTW32_SCHED_LEVEL +#undef PTW32_SCHED_LEVEL_MAX + +#endif /* !_SCHED_H */ +