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}