File: Lock.java

package info (click to toggle)
openjdk-11 11.0.4%2B11-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 757,028 kB
  • sloc: java: 5,016,041; xml: 1,191,974; cpp: 934,731; ansic: 555,697; sh: 24,299; objc: 12,703; python: 3,602; asm: 3,415; makefile: 2,772; awk: 351; sed: 172; perl: 114; jsp: 24; csh: 3
file content (350 lines) | stat: -rw-r--r-- 11,465 bytes parent folder | download | duplicates (2)
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


/* @test
 * @bug 4607272 6814948 6842687
 * @summary Unit test for AsynchronousFileChannel#lock method
 * @key randomness
 */

import java.net.*;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.*;
import static java.nio.file.StandardOpenOption.*;
import java.nio.channels.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Random;
import java.util.concurrent.*;

public class Lock {

    static final Random rand = new Random();

    public static void main(String[] args) throws Exception {
        if (args.length > 0 && args[0].equals("-lockslave")) {
            int port = Integer.parseInt(args[1]);
            runLockSlave(port);
            System.exit(0);
        }

        LockSlaveMirror slave = startLockSlave();
        try {

            // create temporary file
            File blah = File.createTempFile("blah", null);
            blah.deleteOnExit();

            // run tests
            testLockProtocol(blah, slave);
            testAsyncClose(blah, slave);

            // eagerly clean-up
            blah.delete();

        } finally {
            slave.shutdown();
        }
    }

    // test locking protocol
    static void testLockProtocol(File file, LockSlaveMirror slave)
        throws Exception
    {
        FileLock fl;

        // slave VM opens file and acquires exclusive lock
        slave.open(file.getPath()).lock();

        AsynchronousFileChannel ch = AsynchronousFileChannel
            .open(file.toPath(), READ, WRITE);

        // this VM tries to acquire lock
        // (lock should not be acquire until released by slave VM)
        Future<FileLock> result = ch.lock();
        try {
            result.get(2, TimeUnit.SECONDS);
            throw new RuntimeException("Timeout expected");
        } catch (TimeoutException x) {
        }

        // slave VM releases lock
        slave.unlock();

        // this VM should now acquire lock
        fl = result.get();
        fl.release();

        // slave VM acquires lock on range
        slave.lock(0, 10, false);

        // this VM acquires lock on non-overlapping range
        fl = ch.lock(10, 10, false).get();
        fl.release();

        // done
        ch.close();
        slave.close();
    }

    // test close of channel with outstanding lock operation
    static void testAsyncClose(File file, LockSlaveMirror slave) throws Exception {
        // slave VM opens file and acquires exclusive lock
        slave.open(file.getPath()).lock();

        for (int i=0; i<100; i++) {
            AsynchronousFileChannel ch = AsynchronousFileChannel
                .open(file.toPath(), READ, WRITE);

            // try to lock file (should not complete because file is locked by slave)
            Future<FileLock> result = ch.lock();
            try {
                result.get(rand.nextInt(100), TimeUnit.MILLISECONDS);
                throw new RuntimeException("Timeout expected");
            } catch (TimeoutException x) {
            }

            // close channel with lock operation outstanding
            ch.close();

            // operation should complete with AsynchronousCloseException
            try {
                result.get();
                throw new RuntimeException("ExecutionException expected");
            } catch (ExecutionException x) {
                if (!(x.getCause() instanceof AsynchronousCloseException)) {
                    x.getCause().printStackTrace();
                    throw new RuntimeException("AsynchronousCloseException expected");
                }
            }
        }

        slave.close();
    }

    // starts a "lock slave" in another process, returning a mirror object to
    // control the slave
    static LockSlaveMirror startLockSlave() throws Exception {
        ServerSocketChannel ssc = ServerSocketChannel.open()
            .bind(new InetSocketAddress(0));
        int port = ((InetSocketAddress)(ssc.getLocalAddress())).getPort();

        String sep = FileSystems.getDefault().getSeparator();

        String command = System.getProperty("java.home") +
            sep + "bin" + sep + "java";
        String testClasses = System.getProperty("test.classes");
        if (testClasses != null)
            command += " -cp " + testClasses;
        command += " Lock -lockslave " + port;

        Process p = Runtime.getRuntime().exec(command);
        IOHandler.handle(p.getInputStream());
        IOHandler.handle(p.getErrorStream());

        // wait for slave to connect
        SocketChannel sc = ssc.accept();
        return new LockSlaveMirror(sc);
    }

    // commands that the slave understands
    static final String OPEN_CMD    = "open";
    static final String CLOSE_CMD   = "close";
    static final String LOCK_CMD    = "lock";
    static final String UNLOCK_CMD  = "unlock";
    static final char TERMINATOR    = ';';

    // provides a proxy to a "lock slave"
    static class LockSlaveMirror {
        private final SocketChannel sc;

        LockSlaveMirror(SocketChannel sc) {
            this.sc = sc;
        }

        private void sendCommand(String cmd, String... params)
            throws IOException
        {
            for (String s: params) {
                cmd += " " + s;
            }
            cmd += TERMINATOR;

            ByteBuffer buf = Charset.defaultCharset().encode(cmd);
            while (buf.hasRemaining()) {
                sc.write(buf);
            }

            // wait for ack
            buf = ByteBuffer.allocate(1);
            int n = sc.read(buf);
            if (n != 1)
                throw new RuntimeException("Reply expected");
            if (buf.get(0) != TERMINATOR)
                throw new RuntimeException("Terminated expected");
        }

        LockSlaveMirror open(String file) throws IOException {
            sendCommand(OPEN_CMD, file);
            return this;
        }

        void close() throws IOException {
            sendCommand(CLOSE_CMD);
        }

        LockSlaveMirror lock() throws IOException {
            sendCommand(LOCK_CMD);
            return this;
        }


        LockSlaveMirror lock(long position, long size, boolean shared)
            throws IOException
        {
            sendCommand(LOCK_CMD, position + "," + size + "," + shared);
            return this;
        }

        LockSlaveMirror unlock() throws IOException {
            sendCommand(UNLOCK_CMD);
            return this;
        }

        void shutdown() throws IOException {
            sc.close();
        }
    }

    // Helper class to direct process output to the parent System.out
    static class IOHandler implements Runnable {
        private final InputStream in;

        IOHandler(InputStream in) {
            this.in = in;
        }

        static void handle(InputStream in) {
            IOHandler handler = new IOHandler(in);
            Thread thr = new Thread(handler);
            thr.setDaemon(true);
            thr.start();
        }

        public void run() {
            try {
                byte b[] = new byte[100];
                for (;;) {
                    int n = in.read(b);
                    if (n < 0) return;
                    for (int i=0; i<n; i++) {
                        System.out.print((char)b[i]);
                    }
                }
            } catch (IOException ioe) { }
        }
    }

    // slave process that responds to simple commands a socket connection
    static void runLockSlave(int port) throws Exception {

        // establish connection to parent
        SocketChannel sc = SocketChannel.open(new InetSocketAddress(port));
        ByteBuffer buf = ByteBuffer.allocateDirect(1024);

        FileChannel fc = null;
        FileLock fl = null;
        try {
            for (;;) {

                // read command (ends with ";")
                buf.clear();
                int n, last = 0;
                do {
                    n = sc.read(buf);
                    if (n < 0)
                        return;
                    if (n == 0)
                        throw new AssertionError();
                    last += n;
                } while (buf.get(last-1) != TERMINATOR);

                // decode into command and optional parameter
                buf.flip();
                String s = Charset.defaultCharset().decode(buf).toString();
                int sp = s.indexOf(" ");
                String cmd = (sp < 0) ? s.substring(0, s.length()-1) :
                    s.substring(0, sp);
                String param = (sp < 0) ? "" : s.substring(sp+1, s.length()-1);

                // execute
                if (cmd.equals(OPEN_CMD)) {
                    if (fc != null)
                        throw new RuntimeException("File already open");
                    fc = FileChannel.open(Paths.get(param),READ, WRITE);
                }
                if (cmd.equals(CLOSE_CMD)) {
                    if (fc == null)
                        throw new RuntimeException("No file open");
                    fc.close();
                    fc = null;
                    fl = null;
                }
                if (cmd.equals(LOCK_CMD)) {
                    if (fl != null)
                        throw new RuntimeException("Already holding lock");

                    if (param.length() == 0) {
                        fl = fc.lock();
                    } else {
                        String[] values = param.split(",");
                        if (values.length != 3)
                            throw new RuntimeException("Lock parameter invalid");
                        long position = Long.parseLong(values[0]);
                        long size = Long.parseLong(values[1]);
                        boolean shared = Boolean.parseBoolean(values[2]);
                        fl = fc.lock(position, size, shared);
                    }
                }

                if (cmd.equals(UNLOCK_CMD)) {
                    if (fl == null)
                        throw new RuntimeException("Not holding lock");
                    fl.release();
                    fl = null;
                }

                // send reply
                byte[] reply = { TERMINATOR };
                n = sc.write(ByteBuffer.wrap(reply));
            }

        } finally {
            sc.close();
            if (fc != null) fc.close();
        }
    }
}