Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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.

using System;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal unsafe partial class Sys
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MonitorNew")]
internal static extern IntPtr MonitorNew();

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MonitorDelete")]
internal static extern void MonitorDelete(IntPtr monitor);

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MonitorAcquire")]
internal static extern void MonitorAcquire(IntPtr mutex);

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MonitorRelease")]
internal static extern void MonitorRelease(IntPtr mutex);

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MonitorWait")]
internal static extern void MonitorWait(IntPtr monitor);

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MonitorTimedWait")]
internal static extern bool MonitorTimedWait(IntPtr monitor, int timeoutMilliseconds);

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MonitorSignalAndRelease")]
internal static extern void MonitorSignalAndRelease(IntPtr monitor);
}
}
1 change: 1 addition & 0 deletions src/Native/Unix/Common/pal_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#cmakedefine01 HAVE_LINUX_RTNETLINK_H
#cmakedefine01 HAVE_GETDOMAINNAME_SIZET
#cmakedefine01 HAVE_INOTIFY
#cmakedefine01 HAVE_PTHREAD_CONDATTR_SETCLOCK
#cmakedefine01 HAVE_CLOCK_MONOTONIC
#cmakedefine01 HAVE_CLOCK_REALTIME
#cmakedefine01 HAVE_MACH_ABSOLUTE_TIME
Expand Down
1 change: 1 addition & 0 deletions src/Native/Unix/System.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ set(NATIVE_SOURCES
pal_signal.c
pal_string.c
pal_tcpstate.c
pal_threading.c
pal_time.c
pal_uid.c
pal_datetime.c
Expand Down
153 changes: 153 additions & 0 deletions src/Native/Unix/System.Native/pal_threading.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// 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.

#include "pal_config.h"
#include "pal_threading.h"
#include "pal_utilities.h"

#include <stdlib.h>
#include <pthread.h>
#include <time.h>

struct Monitor
{
pthread_mutex_t Mutex;
pthread_cond_t Condition;
};

enum
{
SecondsToNanoSeconds = 1000000000, // 10^9
MilliSecondsToNanoSeconds = 1000000 // 10^6
};

#ifdef NDEBUG
#define assert_no_error(err) ((void)(err))
#else
#define assert_no_error(err) assert((err) == 0)
#endif

Monitor *SystemNative_MonitorNew(void)
{
Monitor *monitor;
int error, initError;

monitor = (Monitor *)malloc(sizeof(struct Monitor));
if (monitor == NULL)
{
return NULL;
}

error = pthread_mutex_init(&monitor->Mutex, NULL);
if (error != 0)
{
free(monitor);
return NULL;
}

#if HAVE_MACH_ABSOLUTE_TIME
// Older versions of OSX don't support CLOCK_MONOTONIC, so we don't use pthread_condattr_setclock. See
// Wait(int32_t timeoutMilliseconds).
initError = pthread_cond_init(&monitor->Condition, NULL);
#elif HAVE_PTHREAD_CONDATTR_SETCLOCK && HAVE_CLOCK_MONOTONIC
pthread_condattr_t conditionAttributes;
error = pthread_condattr_init(&conditionAttributes);
if (error != 0)
{
error = pthread_mutex_destroy(&monitor->Mutex);
assert_no_error(error);
free(monitor);
return NULL;
}

error = pthread_condattr_setclock(&conditionAttributes, CLOCK_MONOTONIC);
assert_no_error(error);

initError = pthread_cond_init(&monitor->Condition, &conditionAttributes);

error = pthread_condattr_destroy(&conditionAttributes);
assert_no_error(error);
#else
#error "Don't know how to perform timed wait on this platform"
#endif

if (initError != 0)
{
error = pthread_mutex_destroy(&monitor->Mutex);
assert_no_error(error);
free(monitor);
return NULL;
}

return monitor;
}

void SystemNative_MonitorDelete(Monitor *monitor)
{
int error;

error = pthread_mutex_destroy(&monitor->Mutex);
assert_no_error(error);
error = pthread_cond_destroy(&monitor->Condition);
assert_no_error(error);
free(monitor);
}

void SystemNative_MonitorAcquire(Monitor *monitor)
{
int error = pthread_mutex_lock(&monitor->Mutex);
assert_no_error(error);
}

void SystemNative_MonitorRelease(Monitor *monitor)
{
int error = pthread_mutex_unlock(&monitor->Mutex);
assert_no_error(error);
}

void SystemNative_MonitorWait(Monitor *monitor)
{
int error = pthread_cond_wait(&monitor->Condition, &monitor->Mutex);
assert_no_error(error);
}

int32_t SystemNative_MonitorTimedWait(Monitor *monitor, int32_t timeoutMilliseconds)
{
int error;

assert(timeoutMilliseconds >= -1);
if (timeoutMilliseconds < 0)
{
SystemNative_MonitorWait(monitor);
return 1;
}

// Calculate the time at which a timeout should occur, and wait. Older versions of OSX don't support clock_gettime with
// CLOCK_MONOTONIC, so we instead compute the relative timeout duration, and use a relative variant of the timed wait.
struct timespec timeout;
#if HAVE_MACH_ABSOLUTE_TIME
timeout.tv_sec = (time_t)(timeoutMilliseconds / 1000);
timeout.tv_nsec = (long)((timeoutMilliseconds % 1000) * MilliSecondsToNanoSeconds);
error = pthread_cond_timedwait_relative_np(&monitor->Condition, &monitor->Mutex, &timeout);
#else
uint64_t nanoseconds;
error = clock_gettime(CLOCK_MONOTONIC, &timeout);
assert_no_error(error);
nanoseconds = ((uint64_t)timeout.tv_sec * SecondsToNanoSeconds) + (uint64_t)timeout.tv_nsec;
nanoseconds += (uint64_t)timeoutMilliseconds * MilliSecondsToNanoSeconds;
timeout.tv_sec = (time_t)(nanoseconds / SecondsToNanoSeconds);
timeout.tv_nsec = (long)(nanoseconds % SecondsToNanoSeconds);
error = pthread_cond_timedwait(&monitor->Condition, &monitor->Mutex, &timeout);
#endif
assert(error == 0 || error == ETIMEDOUT);

return error == 0;
}

void SystemNative_MonitorSignalAndRelease(Monitor *monitor)
{
int error = pthread_cond_signal(&monitor->Condition);
assert_no_error(error);
SystemNative_MonitorRelease(monitor);
}
58 changes: 58 additions & 0 deletions src/Native/Unix/System.Native/pal_threading.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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.

#pragma once

#include "pal_compiler.h"
#include "pal_types.h"

/**
* An opaque type representing the low-level monitor. This is equivalent to
* POSIX threads mutex and condition variable bundled together.
*/
typedef struct Monitor Monitor;

/**
* Allocates a new low-level monitor object and initializes it.
*
* Return NULL if the allocation failed, otherwise an opaque object representing the monitor.
*/
DLLEXPORT Monitor *SystemNative_MonitorNew(void);

/**
* Deallocates the low-level monitor allocated through SystemNative_MonitorNew.
*/
DLLEXPORT void SystemNative_MonitorDelete(Monitor *monitor);

/**
* Acquires the lock on the low-level monitor object.
*/
DLLEXPORT void SystemNative_MonitorAcquire(Monitor *monitor);

/**
* Releases the lock on the low-level monitor object.
*/
DLLEXPORT void SystemNative_MonitorRelease(Monitor *monitor);

/**
* Releases the lock on an object and blocks the current thread until the
* monitor is signalled, then it reacquires the lock.
*/
DLLEXPORT void SystemNative_MonitorWait(Monitor *monitor);

/**
* Releases the lock on an object and blocks the current thread until the
* monitor is signalled, then it reacquires the lock. If the specified time-out
* interval elapses, the wait is aborted.
*
* Returns 1 if the wait succeeded, or 0 if it timed out.
*/
DLLEXPORT int32_t SystemNative_MonitorTimedWait(Monitor *monitor, int32_t timeoutMilliseconds);

/**
* Wakes one thread in the waiting queue (enqueued by either SystemNative_MonitorWait or
* SystemNative_MonitorTimedWait) and releases the lock. Unlike Monitor.Pulse in
* managed code it doesn't reacquire the lock.
*/
DLLEXPORT void SystemNative_MonitorSignalAndRelease(Monitor *monitor);
3 changes: 3 additions & 0 deletions src/Native/Unix/configure.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ include(CheckPrototypeDefinition)
include(CheckStructHasMember)
include(CheckSymbolExists)
include(CheckTypeSize)
include(CheckLibraryExists)

if (CMAKE_SYSTEM_NAME STREQUAL Linux)
set(PAL_UNIX_NAME \"LINUX\")
Expand Down Expand Up @@ -386,6 +387,8 @@ if (CMAKE_SYSTEM_NAME STREQUAL Linux)

endif ()

check_library_exists(pthread pthread_condattr_setclock "" HAVE_PTHREAD_CONDATTR_SETCLOCK)

check_c_source_runs(
"
#include <stdlib.h>
Expand Down