1 /* 2 * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of Oracle nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * This source code is provided to illustrate the usage of a given feature 34 * or technique and has been deliberately simplified. Additional steps 35 * required for a production-quality application, such as security checks, 36 * input validation and proper error handling, might not be present in 37 * this sample code. 38 */ 39 40 /* 41 * Concurrency utilities for JavaScript. These are based on 42 * java.lang and java.util.concurrent API. The following functions 43 * provide a simpler API for scripts. Instead of directly using java.lang 44 * and java.util.concurrent classes, scripts can use functions and 45 * objects exported from here. 46 */ 47 48 // shortcut for j.u.c lock classes 49 var Lock = java.util.concurrent.locks.ReentrantLock; 50 var RWLock = java.util.concurrent.locks.ReentrantReadWriteLock; 51 52 // check if there is a build in sync function, define one if missing 53 if (typeof sync === "undefined") { 54 var sync = function(func, obj) { 55 if (arguments.length < 1 || arguments.length > 2 ) { 56 throw "sync(function [,object]) parameter count mismatch"; 57 } 58 59 var syncobj = (arguments.length == 2 ? obj : this); 60 61 if (!syncobj._syncLock) { 62 syncobj._syncLock = new Lock(); 63 } 64 65 return function() { 66 syncobj._syncLock.lock(); 67 try { 68 func.apply(null, arguments); 69 } finally { 70 syncobj._syncLock.unlock(); 71 } 72 }; 73 }; 74 sync.docString = "synchronize a function, optionally on an object"; 75 } 76 77 /** 78 * Wrapper for java.lang.Object.wait 79 * 80 * can be called only within a sync method 81 */ 82 function wait(object) { 83 var objClazz = java.lang.Class.forName('java.lang.Object'); 84 var waitMethod = objClazz.getMethod('wait', null); 85 waitMethod.invoke(object, null); 86 } 87 wait.docString = "convenient wrapper for java.lang.Object.wait method"; 88 89 /** 90 * Wrapper for java.lang.Object.notify 91 * 92 * can be called only within a sync method 93 */ 94 function notify(object) { 95 var objClazz = java.lang.Class.forName('java.lang.Object'); 96 var notifyMethod = objClazz.getMethod('notify', null); 97 notifyMethod.invoke(object, null); 98 } 99 notify.docString = "convenient wrapper for java.lang.Object.notify method"; 100 101 /** 102 * Wrapper for java.lang.Object.notifyAll 103 * 104 * can be called only within a sync method 105 */ 106 function notifyAll(object) { 107 var objClazz = java.lang.Class.forName('java.lang.Object'); 108 var notifyAllMethod = objClazz.getMethod('notifyAll', null); 109 notifyAllMethod.invoke(object, null); 110 } 111 notifyAll.docString = "convenient wrapper for java.lang.Object.notifyAll method"; 112 113 /** 114 * Creates a java.lang.Runnable from a given script 115 * function. 116 */ 117 Function.prototype.runnable = function() { 118 var args = arguments; 119 var func = this; 120 return new java.lang.Runnable() { 121 run: function() { 122 func.apply(null, args); 123 } 124 } 125 }; 126 127 /** 128 * Executes the function on a new Java Thread. 129 */ 130 Function.prototype.thread = function() { 131 var t = new java.lang.Thread(this.runnable.apply(this, arguments)); 132 t.start(); 133 return t; 134 }; 135 136 /** 137 * Executes the function on a new Java daemon Thread. 138 */ 139 Function.prototype.daemon = function() { 140 var t = new java.lang.Thread(this.runnable.apply(this, arguments)); 141 t.setDaemon(true); 142 t.start(); 143 return t; 144 }; 145 146 /** 147 * Creates a java.util.concurrent.Callable from a given script 148 * function. 149 */ 150 Function.prototype.callable = function() { 151 var args = arguments; 152 var func = this; 153 return new java.util.concurrent.Callable() { 154 call: function() { return func.apply(null, args); } 155 } 156 }; 157 158 /** 159 * Registers the script function so that it will be called exit. 160 */ 161 Function.prototype.atexit = function () { 162 var args = arguments; 163 java.lang.Runtime.getRuntime().addShutdownHook( 164 new java.lang.Thread(this.runnable.apply(this, args))); 165 }; 166 167 /** 168 * Executes the function asynchronously. 169 * 170 * @return a java.util.concurrent.FutureTask 171 */ 172 Function.prototype.future = (function() { 173 // default executor for future 174 var juc = java.util.concurrent; 175 var theExecutor = juc.Executors.newSingleThreadExecutor(); 176 // clean-up the default executor at exit 177 (function() { theExecutor.shutdown(); }).atexit(); 178 return function() { 179 return theExecutor.submit(this.callable.apply(this, arguments)); 180 }; 181 })(); 182 183 /** 184 * Executes a function after acquiring given lock. On return, 185 * (normal or exceptional), lock is released. 186 * 187 * @param lock lock that is locked and unlocked 188 */ 189 Function.prototype.sync = function (lock) { 190 if (arguments.length == 0) { 191 throw "lock is missing"; 192 } 193 var res = new Array(arguments.length - 1); 194 for (var i = 0; i < res.length; i++) { 195 res[i] = arguments[i + 1]; 196 } 197 lock.lock(); 198 try { 199 this.apply(null, res); 200 } finally { 201 lock.unlock(); 202 } 203 }; 204 205 /** 206 * Causes current thread to sleep for specified 207 * number of milliseconds 208 * 209 * @param interval in milliseconds 210 */ 211 function sleep(interval) { 212 java.lang.Thread.sleep(interval); 213 } 214 sleep.docString = "wrapper for java.lang.Thread.sleep method"; 215 216 /** 217 * Schedules a task to be executed once in N milliseconds specified. 218 * 219 * @param callback function or expression to evaluate 220 * @param interval in milliseconds to sleep 221 * @return timeout ID (which is nothing but Thread instance) 222 */ 223 function setTimeout(callback, interval) { 224 if (! (callback instanceof Function)) { 225 callback = new Function(callback); 226 } 227 228 // start a new thread that sleeps given time 229 // and calls callback in an infinite loop 230 return (function() { 231 try { 232 sleep(interval); 233 } catch (x) { } 234 callback(); 235 }).daemon(); 236 } 237 setTimeout.docString = "calls given callback once after specified interval"; 238 239 /** 240 * Cancels a timeout set earlier. 241 * @param tid timeout ID returned from setTimeout 242 */ 243 function clearTimeout(tid) { 244 // we just interrupt the timer thread 245 tid.interrupt(); 246 } 247 clearTimeout.docString = "interrupt a setTimeout timer"; 248 249 /** 250 * Schedules a task to be executed once in 251 * every N milliseconds specified. 252 * 253 * @param callback function or expression to evaluate 254 * @param interval in milliseconds to sleep 255 * @return timeout ID (which is nothing but Thread instance) 256 */ 257 function setInterval(callback, interval) { 258 if (! (callback instanceof Function)) { 259 callback = new Function(callback); 260 } 261 262 // start a new thread that sleeps given time 263 // and calls callback in an infinite loop 264 return (function() { 265 while (true) { 266 try { 267 sleep(interval); 268 } catch (x) { 269 break; 270 } 271 callback(); 272 } 273 }).daemon(); 274 } 275 setInterval.docString = "calls given callback every specified interval"; 276 277 /** 278 * Cancels a timeout set earlier. 279 * @param tid timeout ID returned from setTimeout 280 */ 281 function clearInterval(tid) { 282 // we just interrupt the timer thread 283 tid.interrupt(); 284 } 285 clearInterval.docString = "interrupt a setInterval timer"; 286 287 /** 288 * Simple access to thread local storage. 289 * 290 * Script sample: 291 * 292 * __thread.x = 44; 293 * function f() { 294 * __thread.x = 'hello'; 295 * print(__thread.x); 296 * } 297 * f.thread(); // prints 'hello' 298 * print(__thread.x); // prints 44 in main thread 299 */ 300 var __thread = (function () { 301 var map = new Object(); 302 return new JSAdapter({ 303 __has__: function(name) { 304 return map[name] != undefined; 305 }, 306 __get__: function(name) { 307 if (map[name] != undefined) { 308 return map[name].get(); 309 } else { 310 return undefined; 311 } 312 }, 313 __put__: sync(function(name, value) { 314 if (map[name] == undefined) { 315 var tmp = new java.lang.ThreadLocal(); 316 tmp.set(value); 317 map[name] = tmp; 318 } else { 319 map[name].set(value); 320 } 321 }), 322 __delete__: function(name) { 323 if (map[name] != undefined) { 324 map[name].set(null); 325 } 326 } 327 }); 328 })(); 329 330