Skip to content

Instantly share code, notes, and snippets.

@tqinli
Last active April 19, 2020 21:18
Show Gist options
  • Save tqinli/76b657aeebc48b28f5029152b32c256e to your computer and use it in GitHub Desktop.
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)
// 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