"""
This module provides primitive operations to write multi-threaded programs.
The 'threading' module provides a more convenient interface.
"""

import signal
import sys
from _typeshed import structseq
from collections.abc import Callable
from threading import Thread
from types import TracebackType
from typing import Any, Final, NoReturn, final, overload
from typing_extensions import TypeVarTuple, Unpack

_Ts = TypeVarTuple("_Ts")

error = RuntimeError

def _count() -> int:
    """
    Return the number of currently running Python threads, excluding
    the main thread. The returned number comprises all threads created
    through `start_new_thread()` as well as `threading.Thread`, and not
    yet finished.

    This function is meant for internal and specialized purposes only.
    In most applications `threading.enumerate()` should be used instead.
    """
    ...
@final
class LockType:
    """
    A lock object is a synchronization primitive.  To create a lock,
    call threading.Lock().  Methods are:

    acquire() -- lock the lock, possibly blocking until it can be obtained
    release() -- unlock of the lock
    locked() -- test whether the lock is currently locked

    A lock is not owned by the thread that locked it; another thread may
    unlock it.  A thread attempting to lock a lock that it has already locked
    will block until another thread unlocks it.  Deadlocks may ensue.
    """
    def acquire(self, blocking: bool = True, timeout: float = -1) -> bool:
        """
        Lock the lock.  Without argument, this blocks if the lock is already
        locked (even by the same thread), waiting for another thread to release
        the lock, and return True once the lock is acquired.
        With an argument, this will only block if the argument is true,
        and the return value reflects whether the lock is acquired.
        The blocking operation is interruptible.
        """
        ...
    def release(self) -> None:
        """
        Release the lock, allowing another thread that is blocked waiting for
        the lock to acquire the lock.  The lock must be in the locked state,
        but it needn't be locked by the same thread that unlocks it.
        """
        ...
    def locked(self) -> bool:
        """Return whether the lock is in the locked state."""
        ...
    def acquire_lock(self, blocking: bool = True, timeout: float = -1) -> bool:
        """An obsolete synonym of acquire()."""
        ...
    def release_lock(self) -> None:
        """An obsolete synonym of release()."""
        ...
    def locked_lock(self) -> bool:
        """An obsolete synonym of locked()."""
        ...
    def __enter__(self) -> bool:
        """Lock the lock."""
        ...
    def __exit__(
        self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None
    ) -> None:
        """Release the lock."""
        ...

if sys.version_info >= (3, 13):
    @final
    class _ThreadHandle:
        ident: int

        def join(self, timeout: float | None = None, /) -> None: ...
        def is_done(self) -> bool: ...
        def _set_done(self) -> None: ...

    def start_joinable_thread(
        function: Callable[[], object], handle: _ThreadHandle | None = None, daemon: bool = True
    ) -> _ThreadHandle:
        """
        *For internal use only*: start a new thread.

        Like start_new_thread(), this starts a new thread calling the given function.
        Unlike start_new_thread(), this returns a handle object with methods to join
        or detach the given thread.
        This function is not for third-party code, please use the
        `threading` module instead. During finalization the runtime will not wait for
        the thread to exit if daemon is True. If handle is provided it must be a
        newly created thread._ThreadHandle instance.
        """
        ...
    lock = LockType

@overload
def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]], /) -> int:
    """
    Start a new thread and return its identifier.

    The thread will call the function with positional arguments from the
    tuple args and keyword arguments taken from the optional dictionary
    kwargs.  The thread exits when the function returns; the return value
    is ignored.  The thread will also exit when the function raises an
    unhandled exception; a stack trace will be printed unless the exception
    is SystemExit.
    """
    ...
@overload
def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int:
    """
    Start a new thread and return its identifier.

    The thread will call the function with positional arguments from the
    tuple args and keyword arguments taken from the optional dictionary
    kwargs.  The thread exits when the function returns; the return value
    is ignored.  The thread will also exit when the function raises an
    unhandled exception; a stack trace will be printed unless the exception
    is SystemExit.
    """
    ...

if sys.version_info >= (3, 10):
    def interrupt_main(signum: signal.Signals = ..., /) -> None:
        """
        Simulate the arrival of the given signal in the main thread,
        where the corresponding signal handler will be executed.
        If *signum* is omitted, SIGINT is assumed.
        A subthread can use this function to interrupt the main thread.

        Note: the default signal handler for SIGINT raises ``KeyboardInterrupt``.
        """
        ...

else:
    def interrupt_main() -> None: ...

def exit() -> NoReturn:
    """
    This is synonymous to ``raise SystemExit''.  It will cause the current
    thread to exit silently unless the exception is caught.
    """
    ...
def allocate_lock() -> LockType:
    """
    Create a new lock object. See help(type(threading.Lock())) for
    information about locks.
    """
    ...
def get_ident() -> int:
    """
    Return a non-zero integer that uniquely identifies the current thread
    amongst other threads that exist simultaneously.
    This may be used to identify per-thread resources.
    Even though on some platforms threads identities may appear to be
    allocated consecutive numbers starting at 1, this behavior should not
    be relied upon, and the number should be seen purely as a magic cookie.
    A thread's identity may be reused for another thread after it exits.
    """
    ...
def stack_size(size: int = 0, /) -> int:
    """
    Return the thread stack size used when creating new threads.  The
    optional size argument specifies the stack size (in bytes) to be used
    for subsequently created threads, and must be 0 (use platform or
    configured default) or a positive integer value of at least 32,768 (32k).
    If changing the thread stack size is unsupported, a ThreadError
    exception is raised.  If the specified size is invalid, a ValueError
    exception is raised, and the stack size is unmodified.  32k bytes
     currently the minimum supported stack size value to guarantee
    sufficient stack space for the interpreter itself.

    Note that some platforms may have particular restrictions on values for
    the stack size, such as requiring a minimum stack size larger than 32 KiB or
    requiring allocation in multiples of the system memory page size
    - platform documentation should be referred to for more information
    (4 KiB pages are common; using multiples of 4096 for the stack size is
    the suggested approach in the absence of more specific information).
    """
    ...

TIMEOUT_MAX: float

def get_native_id() -> int:
    """
    Return a non-negative integer identifying the thread as reported
    by the OS (kernel). This may be used to uniquely identify a
    particular thread within a system.
    """
    ...
@final
class _ExceptHookArgs(structseq[Any], tuple[type[BaseException], BaseException | None, TracebackType | None, Thread | None]):
    """
    ExceptHookArgs

    Type used to pass arguments to threading.excepthook.
    """
    if sys.version_info >= (3, 10):
        __match_args__: Final = ("exc_type", "exc_value", "exc_traceback", "thread")

    @property
    def exc_type(self) -> type[BaseException]:
        """Exception type"""
        ...
    @property
    def exc_value(self) -> BaseException | None:
        """Exception value"""
        ...
    @property
    def exc_traceback(self) -> TracebackType | None:
        """Exception traceback"""
        ...
    @property
    def thread(self) -> Thread | None:
        """Thread"""
        ...

_excepthook: Callable[[_ExceptHookArgs], Any]

if sys.version_info >= (3, 12):
    def daemon_threads_allowed() -> bool:
        """
        Return True if daemon threads are allowed in the current interpreter,
        and False otherwise.
        """
        ...

class _local:
    """Thread-local data"""
    def __getattribute__(self, name: str, /) -> Any:
        """Return getattr(self, name)."""
        ...
    def __setattr__(self, name: str, value: Any, /) -> None:
        """Implement setattr(self, name, value)."""
        ...
    def __delattr__(self, name: str, /) -> None:
        """Implement delattr(self, name)."""
        ...
