/* * Copyright (C) by Argonne National Laboratory * See COPYRIGHT in top-level directory */ #include "mpl.h" MPL_SUPPRESS_OSX_HAS_NO_SYMBOLS_WARNING; /* This file currently implements these as a preprocessor if/elif/else sequence. * This has the upside of not doing #includes for .c files or (poorly * named) .i files. It has the downside of making this file large-ish * and a little harder to read in some cases. If this becomes * unmanagable at some point these should be separated back out into * header files and included as needed. [goodell@ 2009-06-24] */ /* Implementation specific function definitions (usually in the form of macros) */ #if MPL_THREAD_PACKAGE_NAME == MPL_THREAD_PACKAGE_WIN /* * struct MPLI_thread_info * * Structure used to pass the user function and data to the intermediate function, MPLI_thread_start. See comment in * MPLI_thread_start() header for more information. */ struct MPLI_thread_info { MPL_thread_func_t func; void *data; }; DWORD WINAPI MPLI_thread_start(LPVOID arg); /* * MPL_thread_create() */ void MPL_thread_create(MPL_thread_func_t func, void *data, MPL_thread_id_t * idp, int *errp) { struct MPLI_thread_info *thread_info; int err = MPL_SUCCESS; thread_info = (struct MPLI_thread_info *) MPL_malloc(sizeof(struct MPLI_thread_info), MPL_MEM_THREAD); if (thread_info != NULL) { thread_info->func = func; thread_info->data = data; *idp = CreateThread(NULL, 0, MPLI_thread_start, thread_info, 0, NULL); if (*idp == NULL) { err = GetLastError(); } } else { err = 1000000000; } if (errp != NULL) { *errp = err; } } /* * MPLI_thread_start() * * Start functions in Windows are expected to return a DWORD. Since our start functions do not return a value we must * use an intermediate function to perform the call to the user's start function and then return a value of 0. */ DWORD WINAPI MPLI_thread_start(LPVOID arg) { struct MPLI_thread_info *thread_info = (struct MPLI_thread_info *) arg; MPL_thread_func_t func = thread_info->func; void *data = thread_info->data; MPL_free(arg); func(data); return 0; } void MPL_thread_exit() { ExitThread(0); } void MPL_thread_self(MPL_thread_id_t * id) { *id = GetCurrentThread(); } void MPL_thread_join(MPL_thread_id_t * id) { WaitForSingleObject(id, INFINITE); } void MPL_thread_same(MPL_thread_id_t * id1, MPL_thread_id_t * id2, int *same) { *same = (*id1 == *id2) ? TRUE : FALSE; } void MPL_thread_yield(void) { Sleep(0); } /* * Mutexes */ void MPL_thread_mutex_create(MPL_thread_mutex_t * mutex, int *err) { *mutex = CreateMutex(NULL, FALSE, NULL); if (err != NULL) { if (*mutex == NULL) { *err = GetLastError(); } else { *err = MPL_SUCCESS; } } } void MPL_thread_mutex_destroy(MPL_thread_mutex_t * mutex, int *err) { BOOL result; result = CloseHandle(*mutex); if (err != NULL) { if (result) { *err = MPL_SUCCESS; } else { *err = GetLastError(); } } } void MPL_thread_mutex_lock(MPL_thread_mutex_t * mutex, int *err, int prio __attribute__ ((unused))) { DWORD result; result = WaitForSingleObject(*mutex, INFINITE); if (err != NULL) { if (result == WAIT_OBJECT_0) { *err = MPL_SUCCESS; } else { if (result == WAIT_FAILED) { *err = GetLastError(); } else { *err = result; } } } } void MPL_thread_mutex_unlock(MPL_thread_mutex_t * mutex, int *err) { BOOL result; result = ReleaseMutex(*mutex); if (err != NULL) { if (result) { *err = MPL_SUCCESS; } else { *err = GetLastError(); } } } /* * Condition Variables */ void MPL_thread_cond_create(MPL_thread_cond_t * cond, int *err) { /* Create a tls slot to store the events used to wakeup each thread in cond_bcast or cond_signal */ MPL_thread_tls_create(NULL, &cond->tls, err); if (err != NULL && *err != MPL_SUCCESS) { return; } /* Create a mutex to protect the fifo queue. This is required because the mutex passed in to the * cond functions need not be the same in each thread. */ MPL_thread_mutex_create(&cond->fifo_mutex, err); if (err != NULL && *err != MPL_SUCCESS) { return; } cond->fifo_head = NULL; cond->fifo_tail = NULL; if (err != NULL) { *err = MPL_SUCCESS; } } void MPL_thread_cond_destroy(MPL_thread_cond_t * cond, int *err) { MPLI_win_thread_cond_fifo_t *iter; while (cond->fifo_head) { iter = cond->fifo_head; cond->fifo_head = cond->fifo_head->next; MPL_free(iter); } MPL_thread_mutex_destroy(&cond->fifo_mutex, err); if (err != NULL && *err != MPL_SUCCESS) { return; } MPL_thread_tls_destroy(&cond->tls, err); /* * if (err != NULL) * { * *err = MPL_SUCCESS; * } */ } void MPL_thread_cond_wait(MPL_thread_cond_t * cond, MPL_thread_mutex_t * mutex, int *err) { HANDLE event; DWORD result; MPL_thread_tls_get(&cond->tls, &event, err); if (err != NULL && *err != MPL_SUCCESS) { return; } if (event == NULL) { event = CreateEvent(NULL, TRUE, FALSE, NULL); if (event == NULL) { if (err != NULL) { *err = GetLastError(); } return; } MPL_thread_tls_set(&cond->tls, event, err); if (err != NULL && *err != MPL_SUCCESS) { return; } } MPL_thread_mutex_lock(&cond->fifo_mutex, err, MPL_THREAD_PRIO_HIGH); if (err != NULL && *err != MPL_SUCCESS) { return; } if (cond->fifo_tail == NULL) { cond->fifo_tail = (MPLI_win_thread_cond_fifo_t *) MPL_malloc(sizeof(MPLI_win_thread_cond_fifo_t), MPL_MEM_THREAD); cond->fifo_head = cond->fifo_tail; } else { cond->fifo_tail->next = (MPLI_win_thread_cond_fifo_t *) MPL_malloc(sizeof(MPLI_win_thread_cond_fifo_t), MPL_MEM_THREAD); cond->fifo_tail = cond->fifo_tail->next; } if (cond->fifo_tail == NULL) { if (err != NULL) { *err = -1; } return; } cond->fifo_tail->event = event; cond->fifo_tail->next = NULL; MPL_thread_mutex_unlock(&cond->fifo_mutex, err); if (err != NULL && *err != MPL_SUCCESS) { return; } MPL_thread_mutex_unlock(mutex, err); if (err != NULL && *err != MPL_SUCCESS) { return; } result = WaitForSingleObject(event, INFINITE); if (err != NULL) { if (result != WAIT_OBJECT_0) { if (result == WAIT_FAILED) { *err = GetLastError(); } else { *err = result; } return; } } result = ResetEvent(event); if (!result && err != NULL) { *err = GetLastError(); return; } MPL_thread_mutex_lock(mutex, err, MPL_THREAD_PRIO_HIGH); /* * if (err != NULL) * { * *err = MPL_SUCCESS; * } */ } void MPL_thread_cond_broadcast(MPL_thread_cond_t * cond, int *err) { MPLI_win_thread_cond_fifo_t *fifo, *temp; MPL_thread_mutex_lock(&cond->fifo_mutex, err, MPL_THREAD_PRIO_HIGH); if (err != NULL && *err != MPL_SUCCESS) { return; } /* remove the fifo queue from the cond variable */ fifo = cond->fifo_head; cond->fifo_head = cond->fifo_tail = NULL; MPL_thread_mutex_unlock(&cond->fifo_mutex, err); if (err != NULL && *err != MPL_SUCCESS) { return; } /* signal each event in the fifo queue */ while (fifo) { if (!SetEvent(fifo->event) && err != NULL) { *err = GetLastError(); /* lost memory */ return; } temp = fifo; fifo = fifo->next; MPL_free(temp); } if (err != NULL) { *err = MPL_SUCCESS; } } void MPL_thread_cond_signal(MPL_thread_cond_t * cond, int *err) { MPLI_win_thread_cond_fifo_t *fifo; MPL_thread_mutex_lock(&cond->fifo_mutex, err, MPL_THREAD_PRIO_HIGH); if (err != NULL && *err != MPL_SUCCESS) { return; } fifo = cond->fifo_head; if (fifo) { cond->fifo_head = cond->fifo_head->next; if (cond->fifo_head == NULL) cond->fifo_tail = NULL; } MPL_thread_mutex_unlock(&cond->fifo_mutex, err); if (err != NULL && *err != MPL_SUCCESS) { return; } if (fifo) { if (!SetEvent(fifo->event) && err != NULL) { *err = GetLastError(); MPL_free(fifo); return; } MPL_free(fifo); } if (err != NULL) { *err = MPL_SUCCESS; } } #endif