// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

/// This library contains native synchronization primitives such as [Mutex]
/// and [ConditionVariable] implemented on top of low-level primitives
/// provided by the OS.
///
/// See OS specific documentation for more details:
///
/// * POSIX man pages (Linux, Android, Mac OS X and iOS X)
///     * `pthread_mutex_lock` and `pthread_mutex_unlock`,
///     * `pthread_cond_wait` and `pthread_cond_signal`.
/// * Windows
///     * [Slim Reader/Writer (SRW) Locks](https://learn.microsoft.com/en-us/windows/win32/sync/slim-reader-writer--srw--locks),
///     * [Condition Variables](https://learn.microsoft.com/en-us/windows/win32/sync/condition-variables),
library;

import 'dart:ffi';
import 'dart:io';

import 'package:ffi/ffi.dart';

import 'sendable.dart';
import 'src/bindings/pthread.dart';
import 'src/bindings/winapi.dart';

part 'posix.dart';
part 'windows.dart';

/// A *mutex* synchronization primitive.
///
/// Mutex can be used to synchronize access to a native resource shared between
/// multiple threads.
///
/// [Mutex] object can not be directly sent to other isolates via a `SendPort`,
/// but it can be converted to a `Sendable<Mutex>` via `asSendable` getter.
///
/// Mutex objects are owned by an isolate which created them.
sealed class Mutex implements Finalizable {
  Mutex._();

  factory Mutex() => Platform.isWindows ? _WindowsMutex() : _PosixMutex();

  /// Acquire exclusive ownership of this mutex.
  ///
  /// If this mutex is already acquired then an attempt to acquire it
  /// blocks the current thread until the mutex is released by the
  /// current owner.
  ///
  /// **Warning**: attempting to hold a mutex across asynchronous suspension
  /// points will lead to undefined behavior and potentially crashes.
  void _lock();

  /// Release exclusive ownership of this mutex.
  ///
  /// It is an error to release ownership of the mutex if it was not
  /// previously acquired.
  void _unlock();

  /// Run the given synchronous `action` under a mutex.
  ///
  /// This function takes exclusive ownership of the mutex, executes `action`
  /// and then releases the mutex. It returns the value returned by `action`.
  ///
  /// **Warning**: you can't combine `runLocked` with an asynchronous code.
  R runLocked<R>(R Function() action) {
    _lock();
    try {
      return action();
    } finally {
      _unlock();
    }
  }

  Sendable<Mutex> get asSendable => Sendable.wrap(
      Platform.isWindows ? _WindowsMutex.fromAddress : _PosixMutex.fromAddress,
      _address);

  int get _address;
}

/// A *condition variable* synchronization primitive.
///
/// Condition variable can be used to synchronously wait for a condition to
/// occur.
///
/// [ConditionVariable] object can not be directly sent to other isolates via a
/// `SendPort`, but it can be converted to a `Sendable<ConditionVariable>`
/// object via [asSendable] getter.
///
/// [ConditionVariable] objects are owned by an isolate which created them.
sealed class ConditionVariable implements Finalizable {
  ConditionVariable._();

  factory ConditionVariable() => Platform.isWindows
      ? _WindowsConditionVariable()
      : _PosixConditionVariable();

  /// Block and wait until another thread calls [notify].
  ///
  /// `mutex` must be a [Mutex] object exclusively held by the current thread.
  /// It will be released and the thread will block until another thread
  /// calls [notify].
  void wait(Mutex mutex);

  /// Wake up at least one thread waiting on this condition variable.
  void notify();

  Sendable<ConditionVariable> get asSendable => Sendable.wrap(
      Platform.isWindows
          ? _WindowsConditionVariable.fromAddress
          : _PosixConditionVariable.fromAddress,
      _address);

  int get _address;
}
