Last active
April 19, 2020 21:18
-
-
Save tqinli/76b657aeebc48b28f5029152b32c256e to your computer and use it in GitHub Desktop.
A repro for pthread_cond_wait/signal's issue of losing signal (using .net core libcoreclrpal.a)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// To compile (have libcoreclrpal.a in current dir) | |
// g++ -g -o pal_cs_repro ./pal_cs_repro.cpp -L. -lcoreclrpal -lpthread -ldl | |
#include <pthread.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <unistd.h> | |
#include <assert.h> | |
typedef int32_t LONG; | |
typedef void VOID; | |
const bool TRUE = 1; | |
const bool FALSE = 0; | |
// Helper to mimic https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockedcompareexchange | |
// NOTE InterlockedCompareExchange as Exchange before Comperand | |
LONG InterlockedCompareExchange( | |
LONG volatile *Destination, | |
LONG Exchange, | |
LONG Comperand) | |
{ | |
LONG result = | |
__sync_val_compare_and_swap( | |
Destination, /* The pointer to a variable whose value is to be compared with. */ | |
Comperand, /* The value to be compared */ | |
Exchange /* The value to be stored */); | |
return result; | |
} | |
LONG InterlockedIncrement(LONG volatile *Destination) | |
{ | |
LONG result = __sync_fetch_and_add(Destination, 1); | |
return result; | |
} | |
LONG InterlockedDecrement(LONG volatile *Destination) | |
{ | |
LONG result = __sync_fetch_and_add(Destination, -1); | |
return result; | |
} | |
extern "C" | |
{ | |
struct CRITICAL_SECTION; | |
// stealing the CRITICAL_SECTION defined in libcoreclrpal.a | |
extern CRITICAL_SECTION virtual_critsec; | |
CRITICAL_SECTION& g_cs = virtual_critsec; | |
VOID InternalInitializeCriticalSection(CRITICAL_SECTION *pcs); | |
VOID InternalDeleteCriticalSection(CRITICAL_SECTION *pcs); | |
VOID PALCEnterCriticalSection(CRITICAL_SECTION *pcs); | |
VOID PALCLeaveCriticalSection(CRITICAL_SECTION *pcs); | |
} | |
// !!!The following code try to repro the pthread_cond_wait/signal bug!!! | |
int total_threads = 4; | |
int g_counter = 0; | |
LONG loop_round = 0; | |
LONG threads_finished = 0; | |
int __thread ThreadId; | |
int threads_in_cs = 0; | |
void *LoopCriticalSectionThread(void *val) | |
{ | |
ThreadId = (int)(long)val; | |
printf("LoopCriticalSectionThread - %d started\n", ThreadId); | |
LONG private_round = 0; | |
while (TRUE) | |
{ | |
// Enter CS | |
PALCEnterCriticalSection(&g_cs); | |
InterlockedIncrement(&threads_in_cs); | |
assert(threads_in_cs == 1); | |
for (int i = 0; i < 1000; ++i) g_counter += ThreadId; | |
// Leavs CS | |
InterlockedDecrement(&threads_in_cs); | |
PALCLeaveCriticalSection(&g_cs); | |
// Mark this thread finished | |
InterlockedIncrement(&threads_finished); | |
// Before start the next iteration, wait until all other | |
// threads to finish this round | |
while (private_round == loop_round) | |
{ | |
sleep(0); | |
} | |
assert(private_round + 1 == loop_round); | |
++private_round; | |
} | |
} | |
void *RefereeThread(void *val) | |
{ | |
printf("RefereeThread - %s started\n", (char *)val); | |
while (TRUE) | |
{ | |
if (total_threads == InterlockedCompareExchange(&threads_finished, 0, total_threads)) | |
{ | |
sleep(0); | |
// if all threads finished, reset the counter | |
// and increment the loop_round, to kick of another round | |
InterlockedIncrement(&loop_round); | |
} | |
sleep(0); | |
} | |
} | |
void MonitorCounter() | |
{ | |
while (TRUE) | |
{ | |
sleep(2); | |
printf("Monitor - g_counter %d, loop_round %d, threads_finished %d\n", g_counter, loop_round, threads_finished); | |
} | |
} | |
int main(int argc, char **argv) | |
{ | |
if (argc >= 2) | |
total_threads = atoi(argv[1]); | |
else | |
total_threads = 1.5 * sysconf(_SC_NPROCESSORS_ONLN); | |
printf("Total Threads Count; %d\n", total_threads); | |
// Initialize CS and try once | |
InternalInitializeCriticalSection(&g_cs); | |
PALCEnterCriticalSection(&g_cs); | |
PALCLeaveCriticalSection(&g_cs); | |
int tid = 0; | |
int iRet; | |
pthread_t threads[total_threads + 1]; | |
// Create threads | |
iRet = pthread_create(&threads[tid++], NULL, RefereeThread, NULL); | |
for (int i = 0; i < total_threads; ++i) | |
{ | |
iRet = pthread_create(&threads[tid++], NULL, LoopCriticalSectionThread, (void *)(long)(i + 1)); | |
assert(iRet == 0); | |
} | |
// Monitor the counter | |
MonitorCounter(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment