1717package com .google .cloud .bigquery .jdbc ;
1818
1919import com .google .api .core .InternalApi ;
20- import com .google .cloud .bigquery .exception .BigQueryJdbcRuntimeException ;
20+ import com .google .cloud .bigquery .jdbc .BigQueryResultSetFinalizers .ResultSetFinalizer ;
21+ import com .google .common .annotations .VisibleForTesting ;
2122import java .lang .ref .Reference ;
2223import java .lang .ref .ReferenceQueue ;
2324import java .util .List ;
2425
2526/**
26- * This class is used to add polling threads required for polling the reference queues associated
27- * with the two ResultSets
27+ * Polls a {@link ReferenceQueue} of result-set phantom references and releases the resources they
28+ * own. One instance (thread) is started per result-set type.
2829 */
2930@ InternalApi
3031class BigQueryDaemonPollingTask extends Thread {
3132
3233 private static final BigQueryJdbcCustomLogger LOG =
3334 new BigQueryJdbcCustomLogger (BigQueryDaemonPollingTask .class .getName ());
3435
35- static ReferenceQueue < BigQueryArrowResultSet > referenceQueueArrowRs ;
36- static ReferenceQueue < BigQueryJsonResultSet > referenceQueueJsonRs ;
37- static List < BigQueryResultSetFinalizers . ArrowResultSetFinalizer > arrowRsFinalizers ;
38- static List <BigQueryResultSetFinalizers . JsonResultSetFinalizer > jsonRsFinalizers ;
36+ // Per-thread state. Each daemon owns exactly one queue and the list that keeps its
37+ // references strongly reachable until they have been processed.
38+ private final ReferenceQueue <?> referenceQueue ;
39+ private final List <? extends Reference <?>> finalizers ;
3940
41+ // Singleton guard: the two daemons are started at most once per JVM.
4042 private static BigQueryDaemonPollingTask arrowDaemon ;
4143 private static BigQueryDaemonPollingTask jsonDaemon ;
42-
4344 private static final Object LOCK = new Object ();
4445
45- private BigQueryDaemonPollingTask (
46- List <BigQueryResultSetFinalizers .ArrowResultSetFinalizer > arrowRsFinalizers ,
47- ReferenceQueue <BigQueryArrowResultSet > referenceQueueArrowRs ) {
48- super ("BigQuery-GC-Daemon-Arrow" );
49- BigQueryDaemonPollingTask .referenceQueueArrowRs = referenceQueueArrowRs ;
50- BigQueryDaemonPollingTask .arrowRsFinalizers = arrowRsFinalizers ;
51- setDaemon (true );
52- }
53-
54- private BigQueryDaemonPollingTask (
55- ReferenceQueue <BigQueryJsonResultSet > referenceQueueJsonRs ,
56- List <BigQueryResultSetFinalizers .JsonResultSetFinalizer > jsonRsFinalizers ) {
57- super ("BigQuery-GC-Daemon-Json" );
58- BigQueryDaemonPollingTask .referenceQueueJsonRs = referenceQueueJsonRs ;
59- BigQueryDaemonPollingTask .jsonRsFinalizers = jsonRsFinalizers ;
46+ @ VisibleForTesting
47+ BigQueryDaemonPollingTask (
48+ String threadName ,
49+ ReferenceQueue <?> referenceQueue ,
50+ List <? extends Reference <?>> finalizers ) {
51+ super (threadName );
52+ this .referenceQueue = referenceQueue ;
53+ this .finalizers = finalizers ;
6054 setDaemon (true );
6155 }
6256
6357 /**
64- * ThreadSafe method which creates two instances of polling task, one each for each type of
65- * ResultSet
58+ * Thread-safe. Starts one polling thread per result-set type. Subsequent calls are no-ops, even
59+ * if different queues are passed.
6660 *
67- * @param referenceQueueArrowRs ReferenceQueue for ArrowResultSet
68- * @param referenceQueueJsonRs ReferenceQueue for JsonResultSet
69- * @param arrowRsFinalizers Finalizer for ArrowResultSet
70- * @param jsonRsFinalizers Finalizer for JsonResultSet
71- * @return true if the tasks were created
61+ * @return true if the daemons were started by this call
7262 */
7363 public static boolean startGcDaemonTask (
7464 ReferenceQueue <BigQueryArrowResultSet > referenceQueueArrowRs ,
@@ -77,45 +67,42 @@ public static boolean startGcDaemonTask(
7767 List <BigQueryResultSetFinalizers .JsonResultSetFinalizer > jsonRsFinalizers ) {
7868 LOG .finest ("++enter++" );
7969 synchronized (LOCK ) {
80- // 2 Background threads will be required to monitor the respective queues
8170 if (arrowDaemon == null && jsonDaemon == null ) {
82- arrowDaemon = new BigQueryDaemonPollingTask (arrowRsFinalizers , referenceQueueArrowRs );
71+ arrowDaemon =
72+ new BigQueryDaemonPollingTask (
73+ "BigQuery-GC-Daemon-Arrow" , referenceQueueArrowRs , arrowRsFinalizers );
8374 arrowDaemon .start ();
8475
85- jsonDaemon = new BigQueryDaemonPollingTask (referenceQueueJsonRs , jsonRsFinalizers );
76+ jsonDaemon =
77+ new BigQueryDaemonPollingTask (
78+ "BigQuery-GC-Daemon-Json" , referenceQueueJsonRs , jsonRsFinalizers );
8679 jsonDaemon .start ();
87-
8880 return true ;
8981 }
9082 }
91- // Task(s) are already initialised
9283 return false ;
9384 }
9485
9586 @ Override
9687 public void run () {
97-
98- Reference <?> reference ;
88+ LOG .finest ("++enter++" );
9989 try {
100- LOG .finest ("++enter++" );
101- // poll for Arrow ResultSets
102- if (referenceQueueArrowRs != null ) {
103-
104- while ((reference = referenceQueueArrowRs .remove ()) != null ) {
105- LOG .fine ("Clearing Arrow ResultSet reference " + referenceQueueArrowRs );
106- ((BigQueryResultSetFinalizers .ArrowResultSetFinalizer ) reference ).finalizeResources ();
107- reference .clear ();
108- }
109- }
110- // poll for JSON ResultSets
111- else if (referenceQueueJsonRs != null ) {
112- while ((reference = referenceQueueJsonRs .remove ()) != null ) {
113- LOG .fine ("Clearing Json ResultSet reference " + referenceQueueJsonRs );
114- ((BigQueryResultSetFinalizers .JsonResultSetFinalizer ) reference ).finalizeResources ();
90+ while (true ) {
91+ Reference <?> reference =
92+ referenceQueue .remove (); // InterruptedException escapes to outer catch
93+ try {
94+ LOG .fine ("Clearing ResultSet reference " + reference );
95+ if (reference instanceof ResultSetFinalizer ) {
96+ ((ResultSetFinalizer ) reference ).finalizeResources ();
97+ } else {
98+ LOG .warning ("Unexpected reference type in GC queue: " + reference .getClass ());
99+ }
100+ } catch (RuntimeException ex ) {
101+ LOG .warning (ex , "Failed to finalize ResultSet resources; continuing" );
102+ } finally {
115103 reference .clear ();
104+ finalizers .remove (reference );
116105 }
117- } else {
118- throw new BigQueryJdbcRuntimeException ("Null Reference Queue" );
119106 }
120107 } catch (InterruptedException ex ) {
121108 LOG .warning (ex , "Interrupted in GC daemon task" );
0 commit comments