1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/**
18 * @author Mikhail Danilov
19 * @version $Revision$
20 */
21package org.apache.harmony.awt.wtk;
22
23import java.util.Hashtable;
24import java.util.LinkedList;
25
26import org.apache.harmony.awt.internal.nls.Messages;
27
28/**
29 * Class synchronizer is to protect AWT state integrity in multithreading environment.
30 * It is supposed to have a child class per native platform.
31 * The only instance is created on the first use of one of the core AWT classes.
32 * Registers WTK on the dispatch thread startup.
33 * It is just a special kind of mutex.
34 *
35 */
36
37public class Synchronizer {
38    //TODO: think about java.util.concurrent use for faster blocking/awaking operations
39    //TODO: think about all synchronized methods. Is there need to synchronize everything?
40
41    /**
42     * This field holds the counter of lock operation.
43     * To free synchronizer unlock method must be called $acquestCounter times.
44     * Equals to 0 when synchronizer is free.
45     */
46    protected int acquestCounter;
47
48    /**
49     * This field holds the owner of synchronizer.
50     * Owner of synchronizer is a last thread that successfully locked synchronizer and
51     * still havn't freed it. Equals to null when synchronizer is free.
52     */
53    protected Thread owner;
54
55    /**
56     * This field holds the wait queue.
57     * Wait queue is a queue where thread wait for synchronizer access.
58     * Empty when synchronizer is free.
59     */
60    protected final LinkedList<Thread> waitQueue = new LinkedList<Thread>();
61
62    /**
63     * The event dispatch thread
64     */
65    protected Thread dispatchThread;
66
67    private final Hashtable<Thread, Integer> storedStates = new Hashtable<Thread, Integer>();
68
69    /**
70     * Acquire the lock for this synchronizer. Nested lock is supported.
71     * If the mutex is already locked by another thread, the current thread will be put
72     * into wait queue until the lock becomes available.
73     * All user threads are served in FIFO order. Dispatch thread has higher priority.
74     * Supposed to be used in Toolkit.lockAWT() only.
75     */
76    public void lock() {
77        synchronized (this) {
78            Thread curThread = Thread.currentThread();
79
80            if (acquestCounter == 0) {
81                acquestCounter = 1;
82                owner = curThread;
83            } else {
84                if (owner == curThread) {
85                    acquestCounter++;
86                } else {
87                    if (curThread == dispatchThread) {
88                        waitQueue.addFirst(curThread);
89                    } else {
90                        waitQueue.addLast(curThread);
91                    }
92                    try {
93                        wait();
94                    } catch (InterruptedException e) {
95                        if (owner != curThread) {
96                            waitQueue.remove(curThread);
97                            // awt.1F=Waiting for resource access thread interrupted not from unlock method.
98                            throw new RuntimeException(Messages
99                                    .getString("awt.1F")); //$NON-NLS-1$
100                        }
101                    }
102                }
103            }
104        }
105    }
106
107    /**
108     * Release the lock for this synchronizer.
109     * If wait queue is not empty the first waiting thread acquires the lock.
110     * Supposed to be used in Toolkit.unlockAWT() only.
111     */
112    public void unlock() {
113        synchronized (this) {
114            if (acquestCounter == 0) {
115                // awt.20=Can't unlock not locked resource.
116                throw new RuntimeException(Messages.getString("awt.20")); //$NON-NLS-1$
117            }
118            if (owner != Thread.currentThread()) {
119                // awt.21=Not owner can't unlock resource.
120                throw new RuntimeException(Messages.getString("awt.21")); //$NON-NLS-1$
121            }
122
123            acquestCounter--;
124            if (acquestCounter == 0) {
125                if (waitQueue.size() > 0) {
126                    acquestCounter = 1;
127                    owner = waitQueue.removeFirst();
128                    owner.interrupt();
129                } else {
130                    owner = null;
131                }
132            }
133        }
134    }
135
136    /**
137     * Stores state of this synchronizer and frees it.
138     * Supposed to be used in Toolkit.unsafeInvokeAndWaitUnderAWTLock() only in pair with
139     * lockAndRestoreState().
140     * Do not call it directly.
141     */
142    public void storeStateAndFree() {
143        synchronized (this) {
144            Thread curThread = Thread.currentThread();
145
146            if (owner != curThread) {
147                // awt.22=Not owner can't free resource.
148                throw new RuntimeException(Messages.getString("awt.22")); //$NON-NLS-1$
149            }
150            if (storedStates.containsKey(curThread)) {
151                // awt.23=One thread can't store state several times in a row.
152                throw new RuntimeException(Messages.getString("awt.23")); //$NON-NLS-1$
153            }
154
155            storedStates.put(curThread, new Integer(acquestCounter));
156            acquestCounter = 1;
157            unlock();
158        }
159    }
160
161    /**
162     * Locks this synchronizer and restores it's state.
163     * Supposed to be used in Toolkit.unsafeInvokeAndWaitUnderAWTLock() only in pair with
164     * storeStateAndFree().
165     * Do not call it directly.
166     */
167    public void lockAndRestoreState() {
168        synchronized (this) {
169            Thread curThread = Thread.currentThread();
170
171            if (owner == curThread) {
172                // awt.24=Owner can't overwrite resource state. Lock operations may be lost.
173                throw new RuntimeException(
174                        Messages.getString("awt.24")); //$NON-NLS-1$
175            }
176            if (!storedStates.containsKey(curThread)) {
177                // awt.25=No state stored for current thread.
178                throw new RuntimeException(Messages.getString("awt.25")); //$NON-NLS-1$
179            }
180
181            lock();
182            acquestCounter = storedStates.get(curThread).intValue();
183            storedStates.remove(curThread);
184        }
185    }
186
187    /**
188     * Sets references to WTK and event dispatch thread.
189     * Called on toolkit startup.
190     *
191     * @param wtk - reference to WTK instance
192     * @param dispatchThread - reference to event dispatch thread
193     */
194    public void setEnvironment(WTK wtk, Thread dispatchThread) {
195        synchronized (this) {
196            this.dispatchThread = dispatchThread;
197        }
198    }
199
200}
201