1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.store; |
7 | |
8 | import java.lang.ref.WeakReference; |
9 | import java.security.AccessControlException; |
10 | import org.h2.Driver; |
11 | import org.h2.engine.Constants; |
12 | import org.h2.engine.Database; |
13 | import org.h2.message.Trace; |
14 | import org.h2.message.TraceSystem; |
15 | |
16 | /** |
17 | * The writer thread is responsible to flush the transaction transaction log |
18 | * from time to time. |
19 | */ |
20 | public class WriterThread implements Runnable { |
21 | |
22 | /** |
23 | * The reference to the database. |
24 | * |
25 | * Thread objects are not garbage collected |
26 | * until they returned from the run() method |
27 | * (even if they where never started) |
28 | * so if the connection was not closed, |
29 | * the database object cannot get reclaimed |
30 | * by the garbage collector if we use a hard reference. |
31 | */ |
32 | private volatile WeakReference<Database> databaseRef; |
33 | |
34 | private int writeDelay; |
35 | private Thread thread; |
36 | private volatile boolean stop; |
37 | |
38 | private WriterThread(Database database, int writeDelay) { |
39 | this.databaseRef = new WeakReference<Database>(database); |
40 | this.writeDelay = writeDelay; |
41 | } |
42 | |
43 | /** |
44 | * Change the write delay |
45 | * |
46 | * @param writeDelay the new write delay |
47 | */ |
48 | public void setWriteDelay(int writeDelay) { |
49 | this.writeDelay = writeDelay; |
50 | } |
51 | |
52 | /** |
53 | * Create and start a new writer thread for the given database. If the |
54 | * thread can't be created, this method returns null. |
55 | * |
56 | * @param database the database |
57 | * @param writeDelay the delay |
58 | * @return the writer thread object or null |
59 | */ |
60 | public static WriterThread create(Database database, int writeDelay) { |
61 | try { |
62 | WriterThread writer = new WriterThread(database, writeDelay); |
63 | writer.thread = new Thread(writer, "H2 Log Writer " + database.getShortName()); |
64 | Driver.setThreadContextClassLoader(writer.thread); |
65 | writer.thread.setDaemon(true); |
66 | return writer; |
67 | } catch (AccessControlException e) { |
68 | // // Google App Engine does not allow threads |
69 | return null; |
70 | } |
71 | } |
72 | |
73 | @Override |
74 | public void run() { |
75 | while (!stop) { |
76 | Database database = databaseRef.get(); |
77 | if (database == null) { |
78 | break; |
79 | } |
80 | int wait = writeDelay; |
81 | try { |
82 | if (database.isFileLockSerialized()) { |
83 | wait = Constants.MIN_WRITE_DELAY; |
84 | database.checkpointIfRequired(); |
85 | } else { |
86 | database.flush(); |
87 | } |
88 | } catch (Exception e) { |
89 | TraceSystem traceSystem = database.getTraceSystem(); |
90 | if (traceSystem != null) { |
91 | traceSystem.getTrace(Trace.DATABASE).error(e, "flush"); |
92 | } |
93 | } |
94 | |
95 | // wait 0 mean wait forever, which is not what we want |
96 | wait = Math.max(wait, Constants.MIN_WRITE_DELAY); |
97 | synchronized (this) { |
98 | while (!stop && wait > 0) { |
99 | // wait 100 ms at a time |
100 | int w = Math.min(wait, 100); |
101 | try { |
102 | wait(w); |
103 | } catch (InterruptedException e) { |
104 | // ignore |
105 | } |
106 | wait -= w; |
107 | } |
108 | } |
109 | } |
110 | databaseRef = null; |
111 | } |
112 | |
113 | /** |
114 | * Stop the thread. This method is called when closing the database. |
115 | */ |
116 | public void stopThread() { |
117 | stop = true; |
118 | synchronized (this) { |
119 | notify(); |
120 | } |
121 | // can't do thread.join(), because this thread might be holding |
122 | // a lock that the writer thread is waiting for |
123 | } |
124 | |
125 | /** |
126 | * Start the thread. This method is called after opening the database |
127 | * (to avoid deadlocks) |
128 | */ |
129 | public void startThread() { |
130 | thread.start(); |
131 | thread = null; |
132 | } |
133 | |
134 | } |