1 if (rio.Thread) { rio.Thread.disable(); }
  2 
  3 rio.Thread = {
  4 	jobs: [],
  5 	groups: {},
  6 	frequency: 100,
  7 	lastPulse: new Date(),
  8 	pulse: function() {
  9 		if (this.jobs.length == 0) {
 10 			this.lastPulse = new Date();
 11 			return;
 12 		}
 13 		
 14 		var start = new Date();
 15 		if (start - this.lastPulse < 50) {
 16 			// rio.log("Thread.skipping_beat - " + (start - this.lastPulse));
 17 			return;
 18 		}
 19 
 20 		try {
 21 			var count = this.frequency;
 22 
 23 			var job = this.jobs.shift();
 24 			while (job) {
 25 				job();
 26 				if (count--) { job = this.jobs.shift(); } else { job = false; }
 27 			}
 28 		} catch(e) {
 29 			rio.warn("thread failed");
 30 		} finally {
 31 			this.lastPulse = new Date();
 32 			var delay = this.lastPulse - start;
 33 
 34 			if (this.jobs.length > 0) {
 35 				if (delay > 100) {
 36 					this.frequency /= 2;
 37 					// rio.log("Thread.frequency changed to: " + this.frequency);
 38 				} else if (delay < 30) {
 39 					this.frequency = (this.frequency * 1.5).ceil();
 40 					// rio.log("Thread.frequency changed to: " + this.frequency);
 41 				}
 42 				// rio.log("Thread.delay - " + delay);
 43 			}
 44 		}
 45 	},
 46 	
 47 	fork: function(fcn, options) {
 48 		var groupName = (options || {}).group;
 49 		if (groupName) {
 50 			var group = this.groups[groupName];
 51 			if (!group) { 
 52 				group = { 
 53 					suspended: options.suspended,
 54 					jobs: []
 55 				};
 56 				this.groups[groupName] = group;
 57 			} else {
 58 				if (options.suspended != undefined) {
 59 					if (group.suspended && !options.suspended) {
 60 						this.resume(groupName);
 61 					} else if (!group.suspended && options.suspended) {
 62 						group.suspended = true;
 63 					}
 64 				}
 65 			}
 66 			
 67 			
 68 			if (!group.suspended) {
 69 				// rio.log("Thread.fork job for group: " + groupName);
 70 				this.jobs.push(fcn);
 71 			} else {
 72 				group.jobs.push(fcn);
 73 			}
 74 
 75 		} else {
 76 			this.jobs.push(fcn);
 77 		}
 78 	},
 79 	
 80 	resume: function(groupName, options) {
 81 		try {
 82 			(function() {
 83 				var group = this.groups[groupName];
 84 				for (var i=0, length=group.jobs.length; i<length; i++) {
 85 					this.jobs.push(group.jobs[i]);
 86 				}
 87 				group.jobs.clear();
 88 				group.suspended = false;
 89 			}.bind(this)).delay((options || {}).delay || 0);
 90 		} catch(e) { 
 91 			rio.warn("Thread.resume failed to resume group: " + groupName);
 92 			throw(e);
 93 		}
 94 	},
 95 	
 96 	suspend: function(groupName) {
 97 		var group = this.groups[groupName];
 98 		if (!group) {
 99 			group = {
100 				jobs: []
101 			};
102 			this.groups[groupName] = group;
103 		}
104 		group.suspended = true;
105 	},
106 	
107 	enable: function() {
108 		if (this.pulseIntervalId) { return; }
109 		this.pulseIntervalId = setInterval(rio.Thread.pulse.bind(rio.Thread), 100);
110 	},
111 	
112 	disable: function() {
113 		if (this.pulseIntervalId) {
114 			clearInterval(this.pulseIntervalId);
115 		}
116 	}
117 };
118 
119 rio.Thread.enable();
120 
121