Protect a function with a lock, that was get from the specified function. If a lock can not be acquire, then
	no function call will be made

	:param lock_fn: callable that returns a lock, with which a function may be protected
	:param blocking: whenever to block operations with lock acquiring
	:param timeout: timeout with which a lock acquiring will be made
	:param raise_exception: whenever to raise an WCriticalSectionError exception if lock can not be acquired

	:return: decorator with which a target function may be protected

def critical_section_dynamic_lock(lock_fn, blocking=True, timeout=None, raise_exception=True):
	""" Protect a function with a lock, that was get from the specified function. If a lock can not be acquire, then
	no function call will be made

	:param lock_fn: callable that returns a lock, with which a function may be protected
	:param blocking: whenever to block operations with lock acquiring
	:param timeout: timeout with which a lock acquiring will be made
	:param raise_exception: whenever to raise an WCriticalSectionError exception if lock can not be acquired

	:return: decorator with which a target function may be protected
	"""

	if blocking is False or timeout is None:
		timeout = -1

	def first_level_decorator(decorated_function):
		def second_level_decorator(original_function, *args, **kwargs):
			lock = lock_fn(*args, **kwargs)
			if lock.acquire(blocking=blocking, timeout=timeout) is True:
				try:
					result = original_function(*args, **kwargs)
					return result
				finally:
					lock.release()
			elif raise_exception is True:
				raise WCriticalSectionError('Unable to lock critical section\n')

		return decorator(second_level_decorator)(decorated_function)
	return first_level_decorator