001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
015: * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
016: * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
017: * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
018: *
019: * Alternatively, the contents of this file may be used under the terms of
020: * either of the GNU General Public License Version 2 or later (the "GPL"),
021: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
022: * in which case the provisions of the GPL or the LGPL are applicable instead
023: * of those above. If you wish to allow use of your version of this file only
024: * under the terms of either the GPL or the LGPL, and not to allow others to
025: * use your version of this file under the terms of the CPL, indicate your
026: * decision by deleting the provisions above and replace them with the notice
027: * and other provisions required by the GPL or the LGPL. If you do not delete
028: * the provisions above, a recipient may use your version of this file under
029: * the terms of any one of the CPL, the GPL or the LGPL.
030: ***** END LICENSE BLOCK *****/package org.jruby.internal.runtime;
031:
032: import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock;
033: import java.lang.ref.WeakReference;
034:
035: import java.util.ArrayList;
036: import java.util.Collections;
037: import java.util.Iterator;
038: import java.util.List;
039: import java.util.Set;
040:
041: import org.jruby.Ruby;
042: import org.jruby.RubyThread;
043: import org.jruby.runtime.ThreadContext;
044: import org.jruby.util.collections.WeakHashSet;
045:
046: public class ThreadService {
047: private Ruby runtime;
048: private ThreadContext mainContext;
049: private ThreadLocal localContext;
050: private ThreadGroup rubyThreadGroup;
051: private Set rubyThreadList;
052: private Thread mainThread;
053:
054: private ReentrantLock criticalLock = new ReentrantLock();
055:
056: public ThreadService(Ruby runtime) {
057: this .runtime = runtime;
058: this .mainContext = ThreadContext.newContext(runtime);
059: this .localContext = new ThreadLocal();
060: this .rubyThreadGroup = new ThreadGroup("Ruby Threads#"
061: + runtime.hashCode());
062: this .rubyThreadList = Collections
063: .synchronizedSet(new WeakHashSet());
064:
065: // Must be called from main thread (it is currently, but this bothers me)
066: mainThread = Thread.currentThread();
067: localContext.set(new WeakReference(mainContext));
068: rubyThreadList.add(mainThread);
069: }
070:
071: public void disposeCurrentThread() {
072: localContext.set(null);
073: }
074:
075: public ThreadContext getCurrentContext() {
076: WeakReference wr = null;
077: ThreadContext context = null;
078:
079: while (context == null) {
080: // loop until a context is available, to clean up weakrefs that might get collected
081: if ((wr = (WeakReference) localContext.get()) == null) {
082: wr = adoptCurrentThread();
083: context = (ThreadContext) wr.get();
084: } else {
085: context = (ThreadContext) wr.get();
086: }
087: if (context == null) {
088: localContext.set(null);
089: }
090: }
091:
092: return context;
093: }
094:
095: private WeakReference adoptCurrentThread() {
096: Thread current = Thread.currentThread();
097:
098: RubyThread.adopt(runtime.getClass("Thread"), current);
099:
100: return (WeakReference) localContext.get();
101: }
102:
103: public RubyThread getMainThread() {
104: return mainContext.getThread();
105: }
106:
107: public void setMainThread(RubyThread thread) {
108: mainContext.setThread(thread);
109: }
110:
111: public synchronized RubyThread[] getActiveRubyThreads() {
112: // all threads in ruby thread group plus main thread
113:
114: synchronized (rubyThreadList) {
115: List rtList = new ArrayList(rubyThreadList.size());
116:
117: for (Iterator iter = rubyThreadList.iterator(); iter
118: .hasNext();) {
119: Thread t = (Thread) iter.next();
120:
121: if (!t.isAlive())
122: continue;
123:
124: RubyThread rt = getRubyThreadFromThread(t);
125: rtList.add(rt);
126: }
127:
128: RubyThread[] rubyThreads = new RubyThread[rtList.size()];
129: rtList.toArray(rubyThreads);
130:
131: return rubyThreads;
132: }
133: }
134:
135: public ThreadGroup getRubyThreadGroup() {
136: return rubyThreadGroup;
137: }
138:
139: public synchronized void registerNewThread(RubyThread thread) {
140: localContext.set(new WeakReference(ThreadContext
141: .newContext(runtime)));
142: getCurrentContext().setThread(thread);
143: // This requires register to be called from within the registree thread
144: rubyThreadList.add(Thread.currentThread());
145: }
146:
147: public synchronized void unregisterThread(RubyThread thread) {
148: rubyThreadList.remove(Thread.currentThread());
149: getCurrentContext().setThread(null);
150: localContext.set(null);
151: }
152:
153: private RubyThread getRubyThreadFromThread(Thread activeThread) {
154: RubyThread rubyThread;
155: if (activeThread instanceof RubyNativeThread) {
156: RubyNativeThread rubyNativeThread = (RubyNativeThread) activeThread;
157: rubyThread = rubyNativeThread.getRubyThread();
158: } else {
159: // main thread
160: rubyThread = mainContext.getThread();
161: }
162: return rubyThread;
163: }
164:
165: public synchronized void setCritical(boolean critical) {
166: if (criticalLock.isHeldByCurrentThread()) {
167: if (critical) {
168: // do nothing
169: } else {
170: criticalLock.unlock();
171: }
172: } else {
173: if (critical) {
174: criticalLock.lock();
175: } else {
176: // do nothing
177: }
178: }
179: }
180:
181: public synchronized boolean getCritical() {
182: return criticalLock.isHeldByCurrentThread();
183: }
184:
185: public void waitForCritical() {
186: if (criticalLock.isLocked()) {
187: criticalLock.lock();
188: criticalLock.unlock();
189: }
190: }
191:
192: }
|