001/* 002 * Copyright (c) 2015 Tada AB and other contributors, as listed below. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the The BSD 3-Clause License 006 * which accompanies this distribution, and is available at 007 * http://opensource.org/licenses/BSD-3-Clause 008 * 009 * Contributors: 010 * Chapman Flack 011 */ 012package org.postgresql.pljava.example.annotation; 013 014import java.sql.Connection; 015import java.sql.DriverManager; 016import java.sql.ResultSet; 017import java.sql.SQLException; 018import java.sql.Statement; 019 020import java.lang.reflect.UndeclaredThrowableException; 021 022import org.postgresql.pljava.annotation.SQLAction; 023import org.postgresql.pljava.annotation.Function; 024 025/** 026 * Test control of access to 1-thread backend by n-thread JVM. 027 * 028 * The "select strictlyNestedTest()" is marked "notFromDDR" because it actually 029 * <em>does</em> deadlock when invoked from within install_jar, though it 030 * succeeds when invoked directly. The explanation may lie in the JNI spec's 031 * caveat that JNI MonitorEnter/MonitorExit functions must be paired with each 032 * other and not arbitrarily mixed with JVM monitorenter/monitorexit bytecodes. 033 * In the present design, that can happen (install_jar uses a synchronized 034 * block to call into the backend when executing DDR commands; DDR command 035 * calling strictlyNestedTest leads to JNI MonitorExit in BEGIN_CALL; perhaps 036 * that does not effectively release the lock taken by the synchronized block). 037 */ 038@SQLAction(implementor="notFromDDR", 039 requires="strictlyNestedTest fn", 040 install="select strictlyNestedTest()" 041) 042public class ThreadTest implements Runnable { 043 /** 044 * Test that another thread can enter SPI while the calling thread is out. 045 * 046 * Create a thread that uses SPI to perform a query and set the value of 047 * {@link #result}. Start and wait for that thread (so this one is clearly 048 * out of the backend the whole time), then return the result. 049 */ 050 @Function(provides="strictlyNestedTest fn") 051 public static String strictlyNestedTest() 052 throws SQLException { 053 ThreadTest tt = new ThreadTest(); 054 Thread t = new Thread( tt); 055 t.start(); 056 while ( true ) 057 { 058 try 059 { 060 t.join(); 061 } 062 catch ( InterruptedException ie ) 063 { 064 continue; 065 } 066 break; 067 } 068 return tt.result; 069 } 070 071 String result; 072 073 public void run() 074 { 075 try 076 { 077 Connection c = DriverManager.getConnection( 078 "jdbc:default:connection"); 079 Statement s = c.createStatement(); 080 ResultSet rs = s.executeQuery( "select version() as version"); 081 rs.next(); 082 result = rs.getString( "version"); 083 } 084 catch ( Exception e ) 085 { 086 if ( e instanceof RuntimeException ) 087 throw (RuntimeException)e; 088 throw new UndeclaredThrowableException( e); 089 } 090 } 091}