function Map() {
	this.map  = new Object();
	// Map API
	this.add = function(k,o){ this.map[k] = o; }
	this.remove	= function(k){ delete this.map[k]; }
	this.get = function(k){ return k==null ? null : this.map[k]; }
	this.first = function(){ return this.get( this.nextKey() ); }
	this.next = function(k){ return this.get( this.nextKey(k) ); }
	this.nextKey = function(k){ 
		for (i in this.map) {
			if (!k) return i;
			if (k==i) k=null;    /*tricky*/
		}
		return null;
	}
}

function Mutex(cmdObject, methodName, methodArguments) {
	// define static variable and method
	if (!Mutex.Wait) Mutex.Wait = new Map();
	Mutex.SLICE = function(cmdID, startID) {
		Mutex.Wait.get(cmdID).attempt(Mutex.Wait.get(startID));
	}
	// define instance method
	this.attempt = function(start) {
		for (var j=start; j; j=Mutex.Wait.next(j.c.id)) {
			if (j.enter || (j.number && (j.number < this.number ||
				(j.number == this.number && j.c.id < this.c.id) ) ) )
			return setTimeout("Mutex.SLICE("+this.c.id+","+j.c.id+")",10);
		}
		this.c[this.methodID](this.methodArgs);	//run with exclusive access
		this.number = 0;            //release exclusive access
		Mutex.Wait.remove(this.c.id);
	}
	// constructor logic
	this.c = cmdObject;
	this.methodID = methodName;
	this.methodArgs = methodArguments;
	Mutex.Wait.add(this.c.id, this);    //enter and number are "false"
	
	this.enter = true;
	this.number = (new Date()).getTime();
	this.enter = false;
	this.attempt(Mutex.Wait.first());
}