Can't get LWJGL 0.98 to Webstart on OS X

Everything works fine until I jar everything up for webstart.

With webstart, the app opens a window (nothing drawn), then promptly falls over with a huge dump. Windows works fine. The Mac dumps. I re-fetched the osx-native and re-jared it, but still the same. Only lwjgl.jar, lwjgl_utils.jar & lwjgl-osx.jar are involved.

The odd thing is when I compile and run on the Mac with Netbeans using liblwjgl.jnilib directly all is fine. I cleared out all my webstart caches, but no change.

If anyone has got 0.98 working via Webstart, could they let me know. Conversely, if your Mac webstart dumps, that’d also be useful input.

The dump’s huge.
Rather than post the lot, here is the problem thread:

To experience this yourself try the latest version of SharpShooter16k (on the Mac)
http://www.circuitswamp.org/projects/sharpshooter16k.html

Hopefully, someone has an idea on this, I’m stumped.

Alan :slight_smile:

Update:

If I go back to using org.lwjgl.util.Display rather than org.lwjgl.opengl.Display and sign the application so that the utility library will work, then the problem goes away. Thus it probably isn’t a LWJGL 0.98 issue, but something more generic. I guess when Cas fixes that library, I can go back to using it -> problem solved (hopefully).

In the meantime, the investigation continues :slight_smile:

I think its a signing problem. Something I’m doing with Display unique to the Mac requires full privileges. That would explain why the code ran under Netbeans, but failed on Webstart (both on the Mac).

It appears to be the act of creating the window that is failing. It does not appear possible to create a display (fullscreen or windowed) unless the app is signed on the Mac.

This may be an instance of Sun JDK bug 4796548 which I believe is still present on Mac OS X. There is a workaround for this in the JOGL workspace which you can find by searching in all files for that bug ID.

Thanks for the suggestion. It does look suspiciously similar
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4796548

Since giving all permissions works round it, I would imagine putting a doPrivileged() around the offending call would work round it (my panacea for all problems).

This isn’t an applet, but the issue looks very similar.

Yup, found the code needed to fix this:
https://jogl.dev.java.net/source/browse/jogl/src/net/java/games/jogl/impl/macosx/MacOSXOnscreenGLContext.java?r1=1.6&r2=1.7

We will implement this shortly - but it wont be available before 0.99.

Thanks for the heads up Ken!

Thanks to all :slight_smile:

Alan :slight_smile:

upon further inspection - I am not sure this is the cause…
the call to GetDrawingSurfaceInfo is made from native code, so we should always have the privileges
besides, it was fixed in 2003?

I was looking at this bit, but haven’t traced back through all the calls yet. However the sun bug report suggets that once the fault has occurred, all further native calls fail, so this could be a symptom rather than the cause.

org.lwjgl.opengl.MacOSXCanvasPeerInfo


private final AWTSurfaceLock awt_surface = new AWTSurfaceLock();

protected void initHandle(Canvas canvas) throws LWJGLException {
  nInitHandle(awt_surface.lockAndGetHandle(canvas), getHandle());
}

private static native void nInitHandle(ByteBuffer surface_buffer, ByteBuffer peer_info_handle) throws LWJGLException;

org.lwjgl.opengl.AWTSurfaceLock


  private static native boolean lockAndInitHandle(ByteBuffer lock_buffer, Canvas canvas) throws LWJGLException;

for the fun of it - can you make a doPrivileged around the lockAndInitHandle method?

I’ll see if I can compile it & subsitute it in the jar.

Meanwhile, I traced it back

org.lwjgl.opengl.Display

public static void create(…) {

peer_info = display_impl.createPeerInfo(pixel_format);
context = new Context(peer_info, shared_drawable != null ? shared_drawable.getContext() : null);

createWindow();
initContext();

}

private static void createWindow() … {
int window_x;
int window_y;

display_impl.createWindow(current_mode, fullscreen, window_x, window_y);
makeCurrent();

setTitle(title);
initControls();
setVSyncEnabled(vsync);

}

public static void makeCurrent() throws LWJGLException {

context.makeCurrent();
}

org.lwjgl.opengl.Context

public synchronized void makeCurrent() throws LWJGLException {
checkAccess();

current_context_local.set(this);
implementation.makeCurrent(peer_info, handle);
GLContext.useContext(this);
}

org.lwjgl.opengl.ContextImplementation

public void makeCurrent(PeerInfo peer_info, ByteBuffer handle) throws LWJGLException;

org.lwjgl.opengl.MacOSXContextImplementation

public void makeCurrent(PeerInfo peer_info, ByteBuffer handle) throws LWJGLException {
ByteBuffer peer_handle = peer_info.lockAndGetHandle();

}

org.lwjgl.opengl.PeerInfo

private final void lockAndInitHandle() throws LWJGLException {
doLockAndInitHandle();
}

public synchronized final ByteBuffer lockAndGetHandle() throws LWJGLException {

doLockAndInitHandle();

}

protected abstract void doLockAndInitHandle() throws LWJGLException;

org.lwjgl.opengl.MacOSXDisplayPeerInfo

protected void doLockAndInitHandle() throws LWJGLException {

MacOSXFrame frame = ((MacOSXDisplay)Display.getImplementation()).getFrame();

Canvas gl_canvas = frame.getCanvas();
initHandle(gl_canvas);

}

org.lwjgl.opengl.MacOSXCanvasPeerInfo

private final AWTSurfaceLock awt_surface = new AWTSurfaceLock();

protected void initHandle(Canvas canvas) throws LWJGLException {
nInitHandle(awt_surface.lockAndGetHandle(canvas), getHandle());
}

private static native void nInitHandle(ByteBuffer surface_buffer, ByteBuffer peer_info_handle) throws LWJGLException;

org.lwjgl.opengl.AWTSurfaceLock

private static native boolean lockAndInitHandle(ByteBuffer lock_buffer, Canvas canvas) throws LWJGLException;

By the way I fixed org.lwjgl.util.Display in CVS.

Cas :slight_smile:

Success!!! ;D

… and you’ve fixed the util library as well :slight_smile:

Alan

(Time to go & enjoy the rest of the day outside)


package org.lwjgl.opengl;

import java.awt.Canvas;
import java.nio.ByteBuffer;
import java.security.*; // ADW *****************************

import org.lwjgl.LWJGLException;
import org.lwjgl.LWJGLUtil;

/**
 * $Id: AWTSurfaceLock.java,v 1.4 2005/05/04 20:59:34 cix_foo Exp $
 *
 * @author elias_naur <elias_naur@users.sourceforge.net>
 * @version $Revision: 1.4 $
 */
final class AWTSurfaceLock {
	private final static int WAIT_DELAY_MILLIS = 100;
	
	private final ByteBuffer lock_buffer;

	public AWTSurfaceLock() {
		lock_buffer = createHandle();
	}
	private static native ByteBuffer createHandle();

	public ByteBuffer lockAndGetHandle(Canvas canvas) throws LWJGLException {
		while (!privilegedLockAndInitHandle(lock_buffer, canvas)) {
			LWJGLUtil.log("Could not get drawing surface info, retrying...");
			try {
				Thread.sleep(WAIT_DELAY_MILLIS);
			} catch (InterruptedException e) {
				LWJGLUtil.log("Interrupted while retrying: " + e);
			}
		}
                
		return lock_buffer;
	}
        
        private boolean privilegedLockAndInitHandle(ByteBuffer lock_buffer, Canvas canvas) throws LWJGLException {
           final ByteBuffer lock_buffer2 = lock_buffer;
           final Canvas canvas2 = canvas;
            try {
                final Object result = AccessController.doPrivileged(
                    new PrivilegedExceptionAction() {
                        public Object run() throws org.lwjgl.LWJGLException { 
                           if (lockAndInitHandle(
                               lock_buffer2, canvas2))
                               return Boolean.TRUE;
                           else
                               return Boolean.FALSE;
                        } 
                    }); 
                return ((Boolean)result).booleanValue();    
            } catch (PrivilegedActionException e) {
                throw (org.lwjgl.LWJGLException) e.getException();
            }
        }

	private static native boolean lockAndInitHandle(ByteBuffer lock_buffer, Canvas canvas) throws LWJGLException;

	protected void unlock() throws LWJGLException {
		nUnlock(lock_buffer);
	}
	private static native void nUnlock(ByteBuffer lock_buffer) throws LWJGLException;
}

Edit: This could be tidied a bit

  • LWJGLException rather than org.lwjgl.LWJGLException
  • Boolean.valueOf(lockAndInitHandle(…)) rather than the if statement

Full Tidied version


/*
 * Copyright (c) 2002-2004 LWJGL Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'LWJGL' nor the names of
 *   its contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.lwjgl.opengl;

import java.awt.Canvas;
import java.nio.ByteBuffer;

import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;

import org.lwjgl.LWJGLException;
import org.lwjgl.LWJGLUtil;

/**
 * $Id: AWTSurfaceLock.java,v 1.4 2005/05/04 20:59:34 cix_foo Exp $
 *
 * @author elias_naur <elias_naur@users.sourceforge.net>
 * @version $Revision: 1.4 $
 */
final class AWTSurfaceLock {
	private final static int WAIT_DELAY_MILLIS = 100;
	
	private final ByteBuffer lock_buffer;

	public AWTSurfaceLock() {
		lock_buffer = createHandle();
	}
	private static native ByteBuffer createHandle();

	public ByteBuffer lockAndGetHandle(Canvas canvas) throws LWJGLException {
		while (!privilegedLockAndInitHandle(lock_buffer, canvas)) {
			LWJGLUtil.log("Could not get drawing surface info, retrying...");
			try {
				Thread.sleep(WAIT_DELAY_MILLIS);
			} catch (InterruptedException e) {
				LWJGLUtil.log("Interrupted while retrying: " + e);
			}
		}
                
		return lock_buffer;
	}
        
	private boolean privilegedLockAndInitHandle(ByteBuffer lock_buffer, Canvas canvas) throws LWJGLException {
		final ByteBuffer lock_buffer2 = lock_buffer;
		final Canvas canvas2 = canvas;
		try {
			final Object result = AccessController.doPrivileged(
				new PrivilegedExceptionAction() {
					public Object run() throws LWJGLException { 
						return Boolean.valueOf(lockAndInitHandle(
							lock_buffer2, canvas2));
					} 
				}); 
			return ((Boolean)result).booleanValue();    
		} catch (PrivilegedActionException e) {
			throw (LWJGLException) e.getException();
		}
	}

	private static native boolean lockAndInitHandle(ByteBuffer lock_buffer, Canvas canvas) throws LWJGLException;

	protected void unlock() throws LWJGLException {
		nUnlock(lock_buffer);
	}
	private static native void nUnlock(ByteBuffer lock_buffer) throws LWJGLException;
}

excellent!
but uhm… why the while loop ? - it fails for some times ?

The problem is that your native code is calling native code in the JDK which calls JNI_FindClass internally. The JDK native code had a bug where it was not requesting elevated privileges before making its own FindClass calls, so upon walking the stack for the security check it was encountering user code which did not have the privileges to see these internal classes. Security rights are granted based on the contents of the Java stack, not the native stack (which the JVM doesn’t have any knowledge of).

Apparently not in Apple’s port of the JDK.

From looking at the above fix it seems that it is not optimal. To work around this bug in the JDK it is only necessary to request elevated privileges during the first call to GetDrawingSurfaceInfo from native code. AccessController.doPrivileged() is expensive so you don’t want to do it every frame.

Try this one Alan_W

/*
 * Copyright (c) 2002-2004 LWJGL Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'LWJGL' nor the names of
 *   its contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.lwjgl.opengl;

import java.awt.Canvas;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;

import org.lwjgl.LWJGLException;
import org.lwjgl.LWJGLUtil;

/**
 * $Id: AWTSurfaceLock.java,v 1.4 2005/05/04 20:59:34 cix_foo Exp $
 *
 * @author elias_naur <elias_naur@users.sourceforge.net>
 * @version $Revision: 1.4 $
 */
final class AWTSurfaceLock {
	private final static int WAIT_DELAY_MILLIS = 100;
	
	private final ByteBuffer lock_buffer;
	
	private boolean needPrivileged = true;

	public AWTSurfaceLock() {
		lock_buffer = createHandle();
	}
	private static native ByteBuffer createHandle();

	public ByteBuffer lockAndGetHandle(final Canvas canvas) throws LWJGLException {
		boolean lockAquired = false;
		while (!lockAquired) {
			// We need to elevate priveleges because of an AWT bug. Please see
			// http://192.18.37.44/forums/index.php?topic=10572 for a discussion.
			// It is only needed on first call, so we avoid it on all subsequent calls
			// due to performance.
			if(needPrivileged) {
				Boolean result = (Boolean) AccessController.doPrivileged(new PrivilegedAction() {
					public Object run() {
						try {
							return lockAndInitHandle(lock_buffer, canvas);
						} catch (LWJGLException le) {
							return false;
						}
					};
				});
				
				// no longer need elevated priveleges
				if(result.booleanValue()) {
					needPrivileged = false;
				}
			} else {
				lockAquired = lockAndInitHandle(lock_buffer, canvas);
			}
			LWJGLUtil.log("Could not get drawing surface info, retrying...");
			try {
				Thread.sleep(WAIT_DELAY_MILLIS);
			} catch (InterruptedException e) {
				LWJGLUtil.log("Interrupted while retrying: " + e);
			}
		}
		return lock_buffer;
	}
	private static native boolean lockAndInitHandle(ByteBuffer lock_buffer, Canvas canvas) throws LWJGLException;

	protected void unlock() throws LWJGLException {
		nUnlock(lock_buffer);
	}
	private static native void nUnlock(ByteBuffer lock_buffer) throws LWJGLException;
}

@Matzon

Your’s is tidier with the ‘final’ keyword, but there’s a couple more improvements: :slight_smile:

  • On the first time around set ‘lock_acquired’, otherwise the code always retries at least once.
  • Catch and throw exceptions from native code. Currently any exceptions inside the native code are swallowed, potentially causing an endless loop.

Alan

How about:


package org.lwjgl.opengl;

import java.awt.Canvas;
import java.nio.ByteBuffer;

import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;

import org.lwjgl.LWJGLException;
import org.lwjgl.LWJGLUtil;

/**
 * $Id: AWTSurfaceLock.java,v 1.4 2005/05/04 20:59:34 cix_foo Exp $
 *
 * @author elias_naur <elias_naur@users.sourceforge.net>
 * @version $Revision: 1.4 $
 */
final class AWTSurfaceLock {
	private final static int WAIT_DELAY_MILLIS = 100;
	
	private final ByteBuffer lock_buffer;
	private boolean firstLockSucceeded = false;

	public AWTSurfaceLock() {
		lock_buffer = createHandle();
	}
	private static native ByteBuffer createHandle();

	public ByteBuffer lockAndGetHandle(Canvas canvas) throws LWJGLException {
		while (!privilegedLockAndInitHandle(canvas)) {
			LWJGLUtil.log("Could not get drawing surface info, retrying...");
			try {
				Thread.sleep(WAIT_DELAY_MILLIS);
			} catch (InterruptedException e) {
				LWJGLUtil.log("Interrupted while retrying: " + e);
			}
		}
                
		return lock_buffer;
	}
        
	private boolean privilegedLockAndInitHandle(final Canvas canvas) throws LWJGLException {
		// Workaround for Sun JDK bug 4796548 which still exists in java for OS X
		if (firstLockSucceeded)
			return lockAndInitHandle(lock_buffer, canvas);
		else
			try {
				final Object result = AccessController.doPrivileged(
					new PrivilegedExceptionAction() {
						public Object run() throws LWJGLException { 
							return Boolean.valueOf(lockAndInitHandle(
								lock_buffer, canvas));
						} 
					});
				firstLockSucceeded = ((Boolean)result).booleanValue();
				return firstLockSucceeded;
			} catch (PrivilegedActionException e) {
				throw (LWJGLException) e.getException();
			}
	}

	private static native boolean lockAndInitHandle(ByteBuffer lock_buffer, Canvas canvas) throws LWJGLException;

	protected void unlock() throws LWJGLException {
		nUnlock(lock_buffer);
	}
	private static native void nUnlock(ByteBuffer lock_buffer) throws LWJGLException;
}

I have tested this.

(The ‘other’ unrelated problem on the Mac is that switching from full-screen to windowed and back causes a crash after a couple of cycles. Always going to fullscreen it appears. It’s a problem with Java2D as well, so I’m not hopeful of finding a fix for that one. This bug report looks interesting http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4922401 - Nope - not even a long sleep after changing modes does any good and other workarounds suggested for Java2D already appear to be in the LWJGL code - I think the only option is to refuse to change fullscreen<=>windowed more than once i.e. if we start in fullscreen, then allow a change to windowed, but refuse to go fullscreen again, at least on the mac)

fixed, thanks to you both!