#
# This module was written in 2007 by S James S Stapleton
# This module is released as PUBLIC DOMAIN
#
# Please respect the creator/author's intent and release all
# modifications as public domain. Thank you.
#

from time import time
from time import sleep
import weakref

import inheritable

import lockholder


class Srlock(inheritable.Irlock):
    """
This is a basic wrapper of the the lock object, that defines an sacquire and srelease function pair. Rather than returning true or
false, the return they return the lockholder type the class was initialized with (greedy or efficient, efficient is the default.)

This also defines a gacquire and eacquire function function, each of which provides a greedy or efficient timeout method.
    """

    __holder_type   = None
    __sleeptime     = None
    __minchecks     = None

    def __init__(self, holder_type=lockholder.SlockE, sleeptime=0.10, minchecks=5):
        """
Initialization:
holder_type: The Slock[G|E] to act as a lock holder for saquire. lockholder.SlockE is the default. It will sleep between lock
             attempts. the G variant does not auto-sleep itself. Either G or E class can fill this argument, or a defined
             subclass (useful for changing the defaults.
sleeptime: If eacquire is called, this will be the amount of time between lock attempts, until the timeout.
minchecks: If sleeptime does not provide enough checks during the timeout period in eacquire, this is used to create a new
           sleeptime that should produce roughly maxtries attempts at the lock (it is not guranteed, especially with small
           timeouts).
        """
        inheritable.Irlock.__init__(self)
        self.__holder_type = holder_type
        self.__sleeptime = sleeptime
        self.__minchecks = minchecks
    #def __init__(self):

    def sacquire(self, timeout=True):
        """
This works as a normal acquire, except there is a timeout period. This may have a boolean translatable value, an integer or a
float.
Integer   - Time out in most timeout seconds
Float     - Time out in timeout seconds
True      - Block until lock is acquired
False     - Nonblocking lock attempt.

This returns a lock holder rather than a lock. The lock is freed if release is called if the lockholder is destroyed, or release is
called on this object.

Due to the garbage collector, the return value should always be kept and not overwritten until after the lock is unenecessary, then
it can have it's .release() function called or be del'ed.


NOTE: only call release() on the returned object, or let it be destroyed, DO NOT CALL ON THIS LOCK if you use this function, as
this lock cannot figure out which is the associated lock holder, if any.
        """
        lh = self.__holder_type(self)

        test = lh.acquire(timeout)
        if(test):
            #record a weakref, so we can kill the holder is the thread is released.
            return lh
        return False
    #def sacquire(self, timeout=True):

    def eacquire(self, timeout=True, sleeptime=None, minchecks=None):
        """
Attempt to acquire a lock on this object in an efficient manner, using the default class mintries/sleeptime values
timeout:
  int/float   - block until timeout after timeout seconds
  True        - block
  False       - don't block

if the lock is acquired, true is returned, otherwise false.

if minchecks is not an integer, the class __minchecks value is used
if sleeptime is not a number, the class __sleeptime value is used.
        """
        if(type(timeout) == int):
            timeout = float(timeout)
        if(type(timeout) == float):
            stop = time() + timeout
            if(type(sleeptime) != int and type(sleeptime) != float):
                sleeptime = self.__sleeptime
            if(type(minchecks) != int):
                minchecks = self.__minchecks
                

            #now get the sleep time, remember calcs take time too, and we would normally miss
            #at least one minimum check, so we add one.
            if(sleeptime < timeout/(self.__minchecks+1)):
                sleeptime = timeout/(self.__minchecks+1)

            while(stop-time() > 0):
                if(inheritable.Irlock.acquire(self, False)):
                    return True
                sleep(sleeptime)
            #while(stop-time() > 0):
            return False
        return inheritable.Irlock.acquire(self, timeout)
        #if(type(timeout) == int)
    #def eacquire(self, timeout=True):

    def gacquire(self, timeout=True):
        """
Attempt to acquire a lock on this object in a greedy manner, without putting itself to sleep. The timeout value may be int, float,
or evaluate to a boolean:
  int/float   - block until timeout after timeout seconds
  True        - block
  False       - don't block

if the lock is acquired, true is returned, otherwise false.
        """
        if(type(timeout) == int):
            timeout = float(timeout)
        if(type(timeout) == float):
            stop = time() + timeout

            while(stop-time() > 0):
                if(inheritable.Irlock.acquire(self, False)):
                    return True
            #while(stop-time() > 0):
            return False
        return inheritable.Irlock.acquire(self, timeout)
        #if(type(timeout) == int)
    #def eacquire(self, timeout=True):
#class Slock(inheritable.Irlock):

