#
# 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 inheritable



class SlockG(object):
    """
This is an object returned from a Slock object on Saquire() and passed to Srelease. When this object is destroyed, is passed to
an Slock's Srelease or has it's own release() called, it will release the lock.

This object is greedy. That means it doesn't play nice and put itself to sleep while waiting for a lock. It's fast, but it will
take resources away from other things that can use them.


lock_callback, unlock_callback and cbd are for debugging. lock_callback and unlock_callback can be assigned functions, these are
called when acquire (only on a successful acquire and __del__ (only if the lock wasn't released) are called specifically. They
take three parameters, this object, the lock object and cbd. These can be used for debugging and logging to find program errors.
    """

    __lock = None
    __locked = None
    lock_callback   = None
    unlock_callback = None
    cbd             = None

    def __init__(self, lock):
        """
Initialize, but do not lock.
        """
        self.__lock = lock
        self.__locked = False
    #def __init__(self, lock):

    def __del__(self):
        """
Unlock if locked at destructor call
        """
        if(self.__locked):
            if(self.unlock_callback):
                self.unlock_callback(self, self.__lock, cbd)
            self.__lock.release()
        #if(self.__locked()):
    #def __destroy__(self):

    def acquire(self, timeout=True):
        """
Acquire the lock held:
timeout values:
  False - do not block, return false on fail, true on success
  True - block indefinetly, return true
  Integer/Float - wait timout seconds before failing, return false on fail, true on success.
        """

        if(type(timeout) == int or
           type(timeout) == float):
            stop = time() + timeout
            while(stop-time() > 0):
                if(self.__lock.acquire(False)):
                    self.__locked = True
                    if(self.lock_callback):
                        self.lock_callback(self, self.__lock, cbd)
                    return True
            #while(stop-time() > 0):
            return False
        self.__locked = self.acquire(timeout)
        if(self.__locked and self.lock_callback):
            self.lock_callback(self, self.__lock, cbd)
        return self.__locked
        #if(type(timeout) == int)
    #def acquire(self, timeout):

    def release(self):
        """
A straight duplicate of Lock's release function.
        """
        self.__locked = False
        self.lock.release()
    #def release(self):

    def unlock(self, lock):
        """
turns of the __locked variable if lock = self.__lock
        """
        if(lock == self.lock):
            self.__locked = False
            self.__lock = None
    #def unlock(self, lock):
#class SlockO(object):



class SlockE(SlockG):
    """
A non-greedy form of SlockG, it has a few more calculations when it grabs a lock, but it also will sleep when not grabbing a lock,
rather than spinning around like an out-of-control CPU hogging gorilla.

minchecks are the minimum number of times a thread will check for a lock, if timeout/sleeptime is less than minchecks, then
sleeptime is set to timeout/minchecks
    """

    __minchecks = None
    __sleeptime = None


    def __init__(self, lock, minchecks=5, sleeptime=0.10):
        """
Initialize, but do not lock.
        """
        self.__lock = lock
        self.__locked = False
        self.__minchecks = minchecks
        self.__sleeptime = sleeptime
    #def __init__(self, lock):

    def acquire(self, timeout=True):
        """
Acquire the lock held:
timeout values:
  False - do not block, return false on fail, true on success
  True - block indefinetly, return true
  Integer/Float - wait timout seconds before failing, return false on fail, true on success.
        """
        
        if(type(timeout) == int):
            timeout = float(timeout)
        if(type(timeout) == float):
            stop = time() + timeout
            sleeptime = self.__sleeptime

            #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(self.__lock.acquire(False)):
                    self.__locked = True
                    if(self.lock_callback):
                        self.lock_callback(self, self.__lock, cbd)
                    return True
                sleep(sleeptime)
            #while(stop-time() > 0):
            return False
        self.__locked = self.acquire(timeout)
        if(self.__locked and self.lock_callback):
            self.lock_callback(self, self.__lock, cbd)
        return self.__locked
        #if(type(timeout) == int)
    #def acquire(self, timeout):
#class SlockE(SlockG):

