module Iowa

	# Iowa::Mutex is a slight modification of the standard Ruby Mutex class.
	# This class changes the array operations used to manage the queue of
	# threads waiting for a lock in order to get better memory management
	# behavior from the array.  This mutex will also not block if the thread
	# holding a lock on a mutex calls lock on that mutex again.  If nested
	# locking occurs, the locks must be matched by an equal number of unlocks
	# before the mutex will actually be unlocked.

	class Mutex
		
		# Creates a new Mutex.
		
		def initialize
			@waiting = []
			@locked = false;
			@nested_locks = 0
			@waiting.taint		# enable tainted comunication
			self.taint
		end

		
		# Returns the thread that holds the lock or +false+ if the mutex is not locked.
		
		def locked?
			@locked
		end

		
		# Attempts to obtain the lock and returns immediately. Returns +true+ if the
		# lock was granted.
		
		def try_lock
			@nested_locks += 1 and return true if @locked == Thread.current
			result = false
			Thread.critical = true
			unless @locked
				@locked = Thread.current
				result = true
			end
			Thread.critical = false
			result
		end


		# Attempts to obtain a lock on the mutex.  Block if a lock can not be
		# obtained immediately and waits until the lock can be obtained.
		# If this thread already holds this lock, returns immediately.
		# Returns the mutex.

		def lock
			if @locked == Thread.current
				@nested_locks += 1
			else
				while (Thread.critical = true; @locked)
					@waiting.unshift Thread.current
					Thread.stop
				end
				@locked = Thread.current
				Thread.critical = false
			end
			self
		end


		# Releases this thread's lock on the mutex and wakes the next thread
		# waiting for the lock, if any.
		
		def unlock
			return unless @locked
			if @nested_locks > 0
				@nested_locks -= 1
			else
				Thread.critical = true
				@locked = false
				begin
					t = @waiting.pop
					t.wakeup if t
				rescue ThreadError
					retry
				end
				Thread.critical = false
				begin
					t.run if t
				rescue ThreadError
				end
			end
			self
		end

		
		# If the mutex is locked, unlocks the mutex, wakes one waiting thread, and
		# yields in a critical section.
		
		def exclusive_unlock
			return unless @locked
			if @nested_locks > 0
				@nested_locks -= 1
			else
				Thread.exclusive do
					@locked = false
					begin
						t = @waiting.pop
						t.wakeup if t
					rescue ThreadError
						retry
					end
					yield
				end
			end
			self
		end

  
		# Obtains a lock, runs the block, and releases the lock when the block
		# completes.  See the example under Mutex.
  
		def synchronize
			lock
			begin
				yield
			ensure
				unlock
			end
 		end

	end
end