From aac560ac261e7a2fde599c6b162f99acd3348fe7 Mon Sep 17 00:00:00 2001 From: Alexandre Petit-Bianco Date: Tue, 10 Jul 2001 17:47:37 -0700 Subject: [PATCH] Makefile.am: Added `java/lang/ThreadLocal.java'. libjava: 2001-07-10 Alexandre Petit-Bianco * Makefile.am: Added `java/lang/ThreadLocal.java'. * Makefile.in: Regenerate. * java/lang/ThreadLocal.java: Initial import. libjava/testsuite: 2001-07-10 Alexandre Petit-Bianco * libjava.lang/TLtest.java: New file. * libjava.lang/TLtest.out: New file. (http://gcc.gnu.org/ml/java-patches/2001-q3/msg00042.html ) From-SVN: r43915 --- libjava/ChangeLog | 6 + libjava/Makefile.am | 1 + libjava/Makefile.in | 2 +- libjava/java/lang/ThreadLocal.java | 165 +++++++++++++++++++++ libjava/testsuite/ChangeLog | 5 + libjava/testsuite/libjava.lang/TLtest.java | 60 ++++++++ libjava/testsuite/libjava.lang/TLtest.out | 2 + 7 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 libjava/java/lang/ThreadLocal.java create mode 100644 libjava/testsuite/libjava.lang/TLtest.java create mode 100644 libjava/testsuite/libjava.lang/TLtest.out diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 1520449cdcf..08d25674983 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,9 @@ +2001-07-10 Alexandre Petit-Bianco + + * Makefile.am: Added `java/lang/ThreadLocal.java'. + * Makefile.in: Regenerate. + * java/lang/ThreadLocal.java: Initial import. + 2001-07-07 Jeff Sturm get() and set() methods) + * only affects the state of the object as seen by the currently + * executing Thread. + *

+ * The first time a ThreadLocal object is accessed on a particular + * Thread (and no state is associated with that Thread yet) + * the state for that Thread is set by executing the method + * initialValue(). + *

+ * An example how you can use this: + *

+ * class Connection {
+ *     private static ThreadLocal owner = new ThreadLocal() {
+ *        public Object initialValue() {
+ *            return("nobody");
+ *        }
+ *     };
+ * ...
+ * }
+ * 
+ * Now all instances of connection can see who the owner of the currently + * executing Thread is by calling owner.get(). By default any + * Thread would be associated with 'nobody'. But the Connection object could + * offer a method that changes the owner associated with the Thread on + * which the method was called by calling owner.put("somebody"). + * (Such an owner changing method should then be guarded by security checks.) + *

+ * When a Thread is garbage collected all references to values of + * the ThreadLocal objects associated with that Thread are removed. + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ +public class ThreadLocal { + + /** + * Trivial container to wrap the stored values. + * Needed to see if the value is null or not yet set. + * If it is not yet set we must call intialValue() once. + * Package local so InheritableThreadLocal can see it. + */ + final static class Value { + final Object value; + + Value(Object value) { + this.value = value; + } + + Object getValue() { + return value; + } + } + + /** + * Maps Threads to Values. Uses a WeakHashMap so if a Thread is garbage + * collected the reference to the Value will disappear. Only the + * set(Thread, Value) and get(Thread) methods + * access it. Since this can happen from multiple Threads simultaniously + * those methods are synchronized. + */ + private final Map valueMap = new WeakHashMap(); + + /** + * Creates a ThreadLocal object without associating any value to it + * yet. + */ + public ThreadLocal() { + } + + /** + * Gets the value associated with the ThreadLocal object for the + * currently executing Thread. If there is no value is associated + * with this Thread yet then the valued returned by the + * initialValue() method is assosiated with this Thread + * and returned. + */ + public Object get() { + Thread currentThread = Thread.currentThread(); + Value v = get(currentThread); + if (v == null) { + v = new Value(initialValue()); + set(currentThread, v); + } + return v.getValue(); + } + + /** + * Gets the Value of this ThreadLocal for a particular Thread. + * It is synchronized so the set(Thread, Value) method cannot + * simultaniously modify the valueMap from another thread. + * Package local so InheritableThreadLocal can access it when a new child + * Thread inherits values from its parent Thread. + */ + synchronized final Value get(Thread thread) { + return (Value)valueMap.get(thread); + } + + /** + * Sets the value associated with the ThreadLocal object for the + * currently executing Thread. This overrides any existing value + * associated with the current Thread and does not call the + * initialValue() method, even if this is the first + * time this Thread accesses this ThreadLocal. + */ + public void set(Object value) { + Thread currentThread = Thread.currentThread(); + Value v = new Value(value); + set(currentThread, v); + } + + /** + * Sets the Value for this ThreadLocal for a particular Thread. + * It is synchronized so the get(Thread) method cannot + * simultaniously read the valueMap from another thread. + * Package local so InheritableThreadLocal can access it when a new child + * Thread inherits values from its parent Thread. + */ + synchronized final void set(Thread thread, Value value) { + valueMap.put(thread, value); + } + + /** + * Called when get() is called and no state is associated + * with the currently executing Thread yet. + *

+ * The default implementation returns null. + */ + protected Object initialValue() { + return null; + } +} diff --git a/libjava/testsuite/ChangeLog b/libjava/testsuite/ChangeLog index cc7acc8506e..5ab3a0f14a6 100644 --- a/libjava/testsuite/ChangeLog +++ b/libjava/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2001-07-10 Alexandre Petit-Bianco + + * libjava.lang/TLtest.java: New file. + * libjava.lang/TLtest.out: New file. + 2001-07-06 Andrew Haley * libjava.lang/Divide_1.java: Add many more test cases. diff --git a/libjava/testsuite/libjava.lang/TLtest.java b/libjava/testsuite/libjava.lang/TLtest.java new file mode 100644 index 00000000000..b7f4115b23f --- /dev/null +++ b/libjava/testsuite/libjava.lang/TLtest.java @@ -0,0 +1,60 @@ +class TLtest extends Thread { + + public static void main (String [] args) { + Data d = new Data (); + new ThreadTest (d, "A").start (); + new ThreadTest (d, "B").start (); + } +} + +class Data { + + private static ThreadLocal owner = new ThreadLocal () { + public Object initialValue () { return ("0"); } + }; + /* A thread will call `set' to set a value it wants an instance + of Data to associate with it and only it. */ + synchronized public void set (String v){owner.set (v);} + /* A thread will call `get' to get a value it wants an instance + of Data to associate with it and only it. */ + synchronized public String get (){return (String)owner.get();} +} + +class ThreadTest extends Thread { + + public Data d; + + ThreadTest (Data d, String name) { + super (name); + this.d = d; + } + + public void run () { + + int value = 0; + int ref = 0; + + for (int i = 0; i < 20; i++) { + + int rand = (int)(Math.random ()*20); + + /* Read `value', ref is kept for comparison */ + value = Integer.parseInt (d.get()); + + /* change `value' and ref by a random number, store `value'. */ + value += rand; ref += rand; + d.set (Integer.toString (value)); + + try { + sleep((int)(Math.random() * 500)); + } catch (InterruptedException e) {} + } + + /* If a thread didn't have private value to attach to the + instance of Data, results wouldn't be the same */ + if (ref == value) + System.out.println ("test OK."); + else + System.out.println ("test failed."); + } +} diff --git a/libjava/testsuite/libjava.lang/TLtest.out b/libjava/testsuite/libjava.lang/TLtest.out new file mode 100644 index 00000000000..951592b6678 --- /dev/null +++ b/libjava/testsuite/libjava.lang/TLtest.out @@ -0,0 +1,2 @@ +test OK. +test OK. -- 2.30.2