summaryrefslogtreecommitdiffstats
path: root/awt/org/apache/harmony/awt/wtk/Synchronizer.java
blob: 3eeaa0b07c1a8905a88f67ecb29e649169c98657 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * @author Mikhail Danilov
 * @version $Revision$
 */
package org.apache.harmony.awt.wtk;

import java.util.Hashtable;
import java.util.LinkedList;

import org.apache.harmony.awt.internal.nls.Messages;

/**
 * Class synchronizer is to protect AWT state integrity in multithreading environment.
 * It is supposed to have a child class per native platform.
 * The only instance is created on the first use of one of the core AWT classes.
 * Registers WTK on the dispatch thread startup.
 * It is just a special kind of mutex.
 *
 */

public class Synchronizer {
    //TODO: think about java.util.concurrent use for faster blocking/awaking operations
    //TODO: think about all synchronized methods. Is there need to synchronize everything?

    /**
     * This field holds the counter of lock operation.
     * To free synchronizer unlock method must be called $acquestCounter times.
     * Equals to 0 when synchronizer is free.
     */
    protected int acquestCounter;

    /**
     * This field holds the owner of synchronizer.
     * Owner of synchronizer is a last thread that successfully locked synchronizer and
     * still havn't freed it. Equals to null when synchronizer is free.
     */
    protected Thread owner;

    /**
     * This field holds the wait queue.
     * Wait queue is a queue where thread wait for synchronizer access.
     * Empty when synchronizer is free.
     */
    protected final LinkedList<Thread> waitQueue = new LinkedList<Thread>();

    /**
     * The event dispatch thread
     */
    protected Thread dispatchThread;

    private final Hashtable<Thread, Integer> storedStates = new Hashtable<Thread, Integer>();

    /**
     * Acquire the lock for this synchronizer. Nested lock is supported.
     * If the mutex is already locked by another thread, the current thread will be put
     * into wait queue until the lock becomes available.
     * All user threads are served in FIFO order. Dispatch thread has higher priority.
     * Supposed to be used in Toolkit.lockAWT() only.
     */
    public void lock() {
        synchronized (this) {
            Thread curThread = Thread.currentThread();

            if (acquestCounter == 0) {
                acquestCounter = 1;
                owner = curThread;
            } else {
                if (owner == curThread) {
                    acquestCounter++;
                } else {
                    if (curThread == dispatchThread) {
                        waitQueue.addFirst(curThread);
                    } else {
                        waitQueue.addLast(curThread);
                    }
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        if (owner != curThread) {
                            waitQueue.remove(curThread);
                            // awt.1F=Waiting for resource access thread interrupted not from unlock method.
                            throw new RuntimeException(Messages
                                    .getString("awt.1F")); //$NON-NLS-1$
                        }
                    }
                }
            }
        }
    }

    /**
     * Release the lock for this synchronizer.
     * If wait queue is not empty the first waiting thread acquires the lock.
     * Supposed to be used in Toolkit.unlockAWT() only.
     */
    public void unlock() {
        synchronized (this) {
            if (acquestCounter == 0) {
                // awt.20=Can't unlock not locked resource.
                throw new RuntimeException(Messages.getString("awt.20")); //$NON-NLS-1$
            }
            if (owner != Thread.currentThread()) {
                // awt.21=Not owner can't unlock resource.
                throw new RuntimeException(Messages.getString("awt.21")); //$NON-NLS-1$
            }

            acquestCounter--;
            if (acquestCounter == 0) {
                if (waitQueue.size() > 0) {
                    acquestCounter = 1;
                    owner = waitQueue.removeFirst();
                    owner.interrupt();
                } else {
                    owner = null;
                }
            }
        }
    }

    /**
     * Stores state of this synchronizer and frees it.
     * Supposed to be used in Toolkit.unsafeInvokeAndWaitUnderAWTLock() only in pair with
     * lockAndRestoreState().
     * Do not call it directly.
     */
    public void storeStateAndFree() {
        synchronized (this) {
            Thread curThread = Thread.currentThread();

            if (owner != curThread) {
                // awt.22=Not owner can't free resource.
                throw new RuntimeException(Messages.getString("awt.22")); //$NON-NLS-1$
            }
            if (storedStates.containsKey(curThread)) {
                // awt.23=One thread can't store state several times in a row.
                throw new RuntimeException(Messages.getString("awt.23")); //$NON-NLS-1$
            }

            storedStates.put(curThread, new Integer(acquestCounter));
            acquestCounter = 1;
            unlock();
        }
    }

    /**
     * Locks this synchronizer and restores it's state.
     * Supposed to be used in Toolkit.unsafeInvokeAndWaitUnderAWTLock() only in pair with
     * storeStateAndFree().
     * Do not call it directly.
     */
    public void lockAndRestoreState() {
        synchronized (this) {
            Thread curThread = Thread.currentThread();

            if (owner == curThread) {
                // awt.24=Owner can't overwrite resource state. Lock operations may be lost.
                throw new RuntimeException(
                        Messages.getString("awt.24")); //$NON-NLS-1$
            }
            if (!storedStates.containsKey(curThread)) {
                // awt.25=No state stored for current thread.
                throw new RuntimeException(Messages.getString("awt.25")); //$NON-NLS-1$
            }

            lock();
            acquestCounter = storedStates.get(curThread).intValue();
            storedStates.remove(curThread);
        }
    }

    /**
     * Sets references to WTK and event dispatch thread.
     * Called on toolkit startup.
     *
     * @param wtk - reference to WTK instance
     * @param dispatchThread - reference to event dispatch thread
     */
    public void setEnvironment(WTK wtk, Thread dispatchThread) {
        synchronized (this) {
            this.dispatchThread = dispatchThread;
        }
    }

}