summaryrefslogtreecommitdiff
path: root/src/pal/src/include/pal/threadsusp.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/src/include/pal/threadsusp.hpp')
-rw-r--r--src/pal/src/include/pal/threadsusp.hpp382
1 files changed, 382 insertions, 0 deletions
diff --git a/src/pal/src/include/pal/threadsusp.hpp b/src/pal/src/include/pal/threadsusp.hpp
new file mode 100644
index 0000000000..e1e85e265c
--- /dev/null
+++ b/src/pal/src/include/pal/threadsusp.hpp
@@ -0,0 +1,382 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*++
+
+
+
+Module Name:
+
+ include/pal/threadsusp.hpp
+
+Abstract:
+ Declarations for thread suspension
+
+
+
+--*/
+
+#ifndef _PAL_THREADSUSP_HPP
+#define _PAL_THREADSUSP_HPP
+
+// Need this ifdef since this header is included by .c files so they can use the diagnostic function.
+#ifdef __cplusplus
+
+// Note: do not include malloc.hpp from this header. The template InternalDelete
+// needs to know the layout of class CPalThread, which includes a member of type
+// CThreadSuspensionInfo, which is defined later in this header, and it is not
+// yet known at this point.
+// If any future change should bring this issue back, the circular dependency can
+// be further broken by making the InternalDelete's CPalThread argument a
+// templatized argument, so that type checking on it takes place only at
+// instantiation time.
+#include "pal/threadinfo.hpp"
+#include "pal/thread.hpp"
+#include "pal/printfcpp.hpp"
+#include "pal/mutex.hpp"
+#include "pal/init.h"
+#if !HAVE_MACH_EXCEPTIONS
+#include <signal.h>
+#endif // !HAVE_MACH_EXCEPTIONS
+#include <semaphore.h>
+#include <sched.h>
+
+// We have a variety of options for synchronizing thread suspensions and resumptions between the requestor and
+// target threads. Analyze the various capabilities given to us by configure and define one of three macros
+// here for simplicity:
+// USE_POSIX_SEMAPHORES
+// USE_SYSV_SEMAPHORES
+// USE_PTHREAD_CONDVARS
+#if HAS_POSIX_SEMAPHORES
+
+// Favor posix semaphores.
+#define USE_POSIX_SEMAPHORES 1
+
+#if HAVE_SYS_SEMAPHORE_H
+#include <sys/semaphore.h>
+#endif // HAVE_SYS_SEMAPHORE_H
+
+#elif HAS_PTHREAD_MUTEXES && HAVE_MACH_EXCEPTIONS
+
+// Can only use the pthread solution if we're not using signals since pthread mutexes are not signal safe.
+#define USE_PTHREAD_CONDVARS 1
+
+#include <pthread.h>
+
+#elif HAS_SYSV_SEMAPHORES
+
+// SYSV semaphores are our last choice since they're shared across processes so it's possible to leak them
+// on abnormal process termination.
+#define USE_SYSV_SEMAPHORES 1
+
+#include <sys/sem.h>
+#include <sys/types.h>
+
+#else
+#error "Don't know how to synchronize thread suspends and resumes on this platform"
+#endif // HAS_POSIX_SEMAPHORES
+
+#include <stdarg.h>
+
+namespace CorUnix
+{
+#ifdef _DEBUG
+#define MAX_TRACKED_CRITSECS 8
+#endif
+
+ PAL_ERROR
+ InternalResumeThread(
+ CPalThread *pthrResumer,
+ HANDLE hTarget,
+ DWORD *pdwSuspendCount
+ );
+
+ class CThreadSuspensionInfo : public CThreadInfoInitializer
+ {
+ private:
+ BOOL m_fPending; // TRUE if a suspension is pending on a thread (because the thread is in an unsafe region)
+ BOOL m_fSelfsusp; // TRUE if thread is self suspending and while thread is self suspended
+ BOOL m_fSuspendedForShutdown; // TRUE once the thread is suspended during PAL cleanup
+ int m_nBlockingPipe; // blocking pipe used for a process that was created suspended
+#ifdef _DEBUG
+ Volatile<LONG> m_lNumThreadsSuspendedByThisThread; // number of threads that this thread has suspended; used for suspension diagnostics
+#endif
+#if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ int m_nSpinlock; // thread's suspension spinlock, which is used to synchronize suspension and resumption attempts
+#else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ pthread_mutex_t m_ptmSuspmutex; // thread's suspension mutex, which is used to synchronize suspension and resumption attempts
+ BOOL m_fSuspmutexInitialized;
+#endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+#if USE_POSIX_SEMAPHORES
+ sem_t m_semSusp; // suspension semaphore
+ sem_t m_semResume; // resumption semaphore
+ BOOL m_fSemaphoresInitialized;
+#elif USE_SYSV_SEMAPHORES
+ // necessary id's and sembuf structures for SysV semaphores
+ int m_nSemsuspid; // id for the suspend semaphore
+ int m_nSemrespid; // id for the resume semaphore
+ struct sembuf m_sbSemwait; // struct representing a wait operation
+ struct sembuf m_sbSempost; // struct representing a post operation
+#elif USE_PTHREAD_CONDVARS
+ pthread_cond_t m_condSusp; // suspension condition variable
+ pthread_mutex_t m_mutexSusp; // mutex associated with the condition above
+ BOOL m_fSuspended; // set to true once the suspend has been acknowledged
+
+ pthread_cond_t m_condResume; // resumption condition variable
+ pthread_mutex_t m_mutexResume; // mutex associated with the condition above
+ BOOL m_fResumed; // set to true once the resume has been acknowledged
+
+ BOOL m_fSemaphoresInitialized;
+#endif // USE_POSIX_SEMAPHORES
+
+ /* Most of the variables above are either accessed by a thread
+ holding the appropriate suspension mutex(es) or are only
+ accessed by their own threads (and thus don't require
+ synchronization).
+
+ m_fPending, m_fSuspendedForShutdown,
+ m_fSuspendSignalSent, and m_fResumeSignalSent
+ may be set by a different thread than the owner and thus
+ require synchronization.
+
+ m_fSelfsusp is set to TRUE only by its own thread but may be later
+ accessed by other threads.
+
+ m_lNumThreadsSuspendedByThisThread is accessed by its owning
+ thread and therefore does not require synchronization. */
+
+#ifdef _DEBUG
+ VOID
+ IncrNumThreadsSuspendedByThisThread(
+ )
+ {
+ InterlockedIncrement(&m_lNumThreadsSuspendedByThisThread);
+ };
+
+ VOID
+ DecrNumThreadsSuspendedByThisThread(
+ )
+ {
+ InterlockedDecrement(&m_lNumThreadsSuspendedByThisThread);
+ };
+#endif
+
+ VOID
+ AcquireSuspensionLocks(
+ CPalThread *pthrSuspender,
+ CPalThread *pthrTarget
+ );
+
+ VOID
+ ReleaseSuspensionLocks(
+ CPalThread *pthrSuspender,
+ CPalThread *pthrTarget
+ );
+
+#if USE_POSIX_SEMAPHORES
+ sem_t*
+ GetSuspendSemaphore(
+ void
+ )
+ {
+ return &m_semSusp;
+ };
+
+ sem_t*
+ GetResumeSemaphore(
+ void
+ )
+ {
+ return &m_semResume;
+ };
+#elif USE_SYSV_SEMAPHORES
+ int
+ GetSuspendSemaphoreId(
+ void
+ )
+ {
+ return m_nSemsuspid;
+ };
+
+ sembuf*
+ GetSemaphorePostBuffer(
+ void
+ )
+ {
+ return &m_sbSempost;
+ };
+#endif // USE_POSIX_SEMAPHORES
+
+#if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ LONG*
+ GetSuspensionSpinlock(
+ void
+ )
+ {
+ return &m_nSpinlock;
+ }
+#else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ pthread_mutex_t*
+ GetSuspensionMutex(
+ void
+ )
+ {
+ return &m_ptmSuspmutex;
+ }
+#endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+
+ void
+ SetSuspPending(
+ BOOL fPending
+ )
+ {
+ m_fPending = fPending;
+ };
+
+ BOOL
+ GetSuspPending(
+ void
+ )
+ {
+ return m_fPending;
+ };
+
+ void
+ SetSelfSusp(
+ BOOL fSelfsusp
+ )
+ {
+ m_fSelfsusp = fSelfsusp;
+ };
+
+ BOOL
+ GetSelfSusp(
+ void
+ )
+ {
+ return m_fSelfsusp;
+ };
+
+ void
+ PostOnSuspendSemaphore();
+
+ void
+ WaitOnSuspendSemaphore();
+
+ void
+ PostOnResumeSemaphore();
+
+ void
+ WaitOnResumeSemaphore();
+
+ static
+ BOOL
+ TryAcquireSuspensionLock(
+ CPalThread* pthrTarget
+ );
+
+ int GetBlockingPipe(
+ void
+ )
+ {
+ return m_nBlockingPipe;
+ };
+
+ public:
+ virtual PAL_ERROR InitializePreCreate();
+
+ CThreadSuspensionInfo()
+ : m_fPending(FALSE)
+ , m_fSelfsusp(FALSE)
+ , m_fSuspendedForShutdown(FALSE)
+ , m_nBlockingPipe(-1)
+#ifdef _DEBUG
+ , m_lNumThreadsSuspendedByThisThread(0)
+#endif // _DEBUG
+#if !DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
+ , m_fSuspmutexInitialized(FALSE)
+#endif
+#if USE_POSIX_SEMAPHORES || USE_PTHREAD_CONDVARS
+ , m_fSemaphoresInitialized(FALSE)
+#endif
+ {
+ InitializeSuspensionLock();
+ };
+
+ virtual ~CThreadSuspensionInfo();
+
+#ifdef _DEBUG
+ LONG
+ GetNumThreadsSuspendedByThisThread(
+ void
+ )
+ {
+ return m_lNumThreadsSuspendedByThisThread;
+ };
+#endif // _DEBUG
+
+#if USE_SYSV_SEMAPHORES
+ void
+ DestroySemaphoreIds(
+ void
+ );
+#endif
+ void
+ SetSuspendedForShutdown(
+ BOOL fSuspendedForShutdown
+ )
+ {
+ m_fSuspendedForShutdown = fSuspendedForShutdown;
+ };
+
+ BOOL
+ GetSuspendedForShutdown(
+ void
+ )
+ {
+ return m_fSuspendedForShutdown;
+ };
+
+ void
+ AcquireSuspensionLock(
+ CPalThread *pthrCurrent
+ );
+
+ void
+ ReleaseSuspensionLock(
+ CPalThread *pthrCurrent
+ );
+
+ PAL_ERROR
+ InternalSuspendNewThreadFromData(
+ CPalThread *pThread
+ );
+
+ PAL_ERROR
+ InternalResumeThreadFromData(
+ CPalThread *pthrResumer,
+ CPalThread *pthrTarget,
+ DWORD *pdwSuspendCount
+ );
+
+ VOID InitializeSuspensionLock();
+
+ void SetBlockingPipe(
+ int nBlockingPipe
+ )
+ {
+ m_nBlockingPipe = nBlockingPipe;
+ };
+ };
+} //end CorUnix
+
+extern const BYTE WAKEUPCODE; // use for pipe reads during self suspend.
+#endif // __cplusplus
+
+#ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
+extern LONG g_ssSuspensionLock;
+#endif
+
+#endif // _PAL_THREADSUSP_HPP
+