from __future__ import absolute_import, unicode_literals

import threading
import functools


def atomize(f, lock=None):
    """
    Decorate a function with a reentrant lock to prevent multiple
    threads from calling said thread simultaneously.
    """
    lock = lock or threading.RLock()

    @functools.wraps(f)
    def exec_atomic(*args, **kwargs):
        lock.acquire()
        try:
            return f(*args, **kwargs)
        finally:
            lock.release()

    return exec_atomic


class AtomicGuard(object):
    """
    A decorator that can be applied to multiple functions/methods to
    prevent more than one of them from being entered at any one time.
    """

    def __init__(self):
        self.lock = threading.RLock()

    def __call__(self, f):
        return atomize(f, lock=self.lock)
