# Path: offset/sync/atomic.py
# class AtomicLong(object):
#     def __init__(self, initial_value):
#         self._storage = ffi.new('long *', initial_value)
# 
#     def __repr__(self):
#         return '<{0} at 0x{1:x}: {2!r}>'.format(
#             self.__class__.__name__, id(self), self.value)
# 
#     @property
#     def value(self):
#         return self._storage[0]
# 
#     @value.setter
#     def value(self, new):
#         lib.long_bool_compare_and_swap(self._storage, self.value, new)
# 
#     def add(self, delta):
#         """ atomically adds delta and returns the new value """
#         if delta >= 0:
#             lib.long_add_and_fetch(self._storage, delta)
#         else:
#             lib.long_sub_and_fetch(self._storage, abs(delta))
# 
#         return self._storage[0]
# 
# 
#     def __iadd__(self, inc):
#         lib.long_add_and_fetch(self._storage, inc)
#         return self
# 
#     def __isub__(self, dec):
#         lib.long_sub_and_fetch(self._storage, dec)
#         return self
# 
#     def __eq__(self, other):
#         if isinstance(other, AtomicLong):
#             return self.value == other.value
#         else:
#             return self.value == other
# 
#     def __ne__(self, other):
#         return not (self == other)
# 
#     def __lt__(self, other):
#         if isinstance(other, AtomicLong):
#             return self.value < other.value
#         else:
#             return self.value < other
# 
# Path: offset/core/context.py
# def park(self, g=None):
#     g = g or proc.current()
#     g.sleeping = True
#     try:
#         self.runq.remove(g)
#     except ValueError:
#         pass
#     self.schedule()
# 
# Path: offset/core/proc.py
# class ProcExit(Exception):
# class Proc(object):
# def current():
#     def __init__(self, m, func, args, kwargs):
#         def _run():
#     def switch(self):
#     def throw(self, *args):
#     def park(self):
#     def ready(self):
#     def is_alive(self):
#     def terminate(self):
#     def __eq__(self, other):
# def _create_main_proc():


from collections import deque
from .atomic import AtomicLong
from ..core.context import park
from ..core import proc
    http://swtch.com/semaphore.pdf

    Comment and code based on the Go code:
    http://golang.org/src/pkg/runtime/sema.goc
    """

    def __init__(self, value=0):
        self.sema = AtomicLong(value)
        self.nwait = AtomicLong(1)
        self.waiters = deque()

    def can_acquire(self):
        if self.sema > 0:
            self.sema -= 1
            return True
        return False

    def acquire(self):
        if self.can_acquire():
            return

        t0 = 0
        releasetime = 0

        while True:
            self.nwait += 1
            self.waiters.append(proc.current())

            if self.can_acquire():
                self.nwait -= 1

# Path: offset/sync/atomic.py
# class AtomicLong(object):
#     def __init__(self, initial_value):
#         self._storage = ffi.new('long *', initial_value)
# 
#     def __repr__(self):
#         return '<{0} at 0x{1:x}: {2!r}>'.format(
#             self.__class__.__name__, id(self), self.value)
# 
#     @property
#     def value(self):
#         return self._storage[0]
# 
#     @value.setter
#     def value(self, new):
#         lib.long_bool_compare_and_swap(self._storage, self.value, new)
# 
#     def add(self, delta):
#         """ atomically adds delta and returns the new value """
#         if delta >= 0:
#             lib.long_add_and_fetch(self._storage, delta)
#         else:
#             lib.long_sub_and_fetch(self._storage, abs(delta))
# 
#         return self._storage[0]
# 
# 
#     def __iadd__(self, inc):
#         lib.long_add_and_fetch(self._storage, inc)
#         return self
# 
#     def __isub__(self, dec):
#         lib.long_sub_and_fetch(self._storage, dec)
#         return self
# 
#     def __eq__(self, other):
#         if isinstance(other, AtomicLong):
#             return self.value == other.value
#         else:
#             return self.value == other
# 
#     def __ne__(self, other):
#         return not (self == other)
# 
#     def __lt__(self, other):
#         if isinstance(other, AtomicLong):
#             return self.value < other.value
#         else:
#             return self.value < other
# 
# Path: offset/core/context.py
# def park(self, g=None):
#     g = g or proc.current()
#     g.sleeping = True
#     try:
#         self.runq.remove(g)
#     except ValueError:
#         pass
#     self.schedule()
# 
# Path: offset/core/proc.py
# class ProcExit(Exception):
# class Proc(object):
# def current():
#     def __init__(self, m, func, args, kwargs):
#         def _run():
#     def switch(self):
#     def throw(self, *args):
#     def park(self):
#     def ready(self):
#     def is_alive(self):
#     def terminate(self):
#     def __eq__(self, other):
# def _create_main_proc():


# Path: offset/sync/sema.py
from collections import deque
from .atomic import AtomicLong
from ..core.context import park
from ..core import proc

    http://swtch.com/semaphore.pdf

    Comment and code based on the Go code:
    http://golang.org/src/pkg/runtime/sema.goc
    """

    def __init__(self, value=0):
        self.sema = AtomicLong(value)
        self.nwait = AtomicLong(1)
        self.waiters = deque()

    def can_acquire(self):
        if self.sema > 0:
            self.sema -= 1
            return True
        return False

    def acquire(self):
        if self.can_acquire():
            return

        t0 = 0
        releasetime = 0

        while True:
            self.nwait += 1
            self.waiters.append(proc.current())

            if self.can_acquire():
                self.nwait -= 1

                self.waiters.remove(proc.current())