changeset 189:dd0fc7e6d5f9

8129107: DIO UART xxxRoundCompleted() should be notified again if the corresponding ByteBuffer still has no remaining Summary: Refactoring of UARTImpl to support custom event messaging Reviewed-by: alkonsta
author snazarki
date Thu, 18 Jun 2015 13:38:19 +0300
parents a4733ce6bec9
children f1a65e783681
files src/se/classes/com/oracle/dio/impl/EventQueueManager.java src/share/classes/com/oracle/dio/impl/AbstractPeripheral.java src/share/classes/com/oracle/dio/uart/impl/UARTImpl.java
diffstat 3 files changed, 216 insertions(+), 141 deletions(-) [+]
line wrap: on
line diff
--- a/src/se/classes/com/oracle/dio/impl/EventQueueManager.java	Thu Jun 18 13:20:08 2015 +0300
+++ b/src/se/classes/com/oracle/dio/impl/EventQueueManager.java	Thu Jun 18 13:38:19 2015 +0300
@@ -68,6 +68,11 @@
         listenerRegistry.remove(getHash(eventType, eventSubType, nativeHandle));
     }
 
+    public void postEvent(AbstractPeripheral receiver, int subEventType, DeviceEvent event) {
+        final Tuple tuple = new Tuple(receiver.getDescriptor().getInterface(), subEventType, receiver, event);
+        queue.postEvent(tuple);
+    }
+
 
     /**
      * This method is called by EventQueue.dispatch(). Each call is made on a
@@ -75,14 +80,19 @@
      * @param event a previously queued event to handle
      */
     public boolean handleEvent(Event event) {
-        IntBuffer payload = ByteBuffer.wrap(event.getPayload()).asIntBuffer();
-        // all data are stored in big endia format
-        int handle = payload.get();
-        int subEvent  = payload.get();
-        int data   = payload.get();
-        AbstractPeripheral handler = listenerRegistry.get(getHash(event.getType().hashCode(), subEvent, handle));
-        if (handler != null) {
-            handler.processNativeEvent(subEvent, data);
+        if (event instanceof Tuple) {
+            Tuple tuple = (Tuple)event;
+            tuple.receiver.processDeviceEvent(tuple.eventType, tuple.event);
+        } else {
+            IntBuffer payload = ByteBuffer.wrap(event.getPayload()).asIntBuffer();
+            // all data are stored in big endian format
+            int handle = payload.get();
+            int subEvent  = payload.get();
+            int data   = payload.get();
+            AbstractPeripheral handler = listenerRegistry.get(getHash(event.getType().hashCode(), subEvent, handle));
+            if (handler != null) {
+                handler.processNativeEvent(subEvent, data);
+            }
         }
         return true;
     }
@@ -90,4 +100,17 @@
     public boolean isDispatchThread() {
         return queue.getEventDispatchThread().equals(Thread.currentThread());
     }
+
+    /* simple holder for custom data */
+    private final class Tuple extends Event {
+        private final DeviceEvent event;
+        private final int eventType;
+        private final AbstractPeripheral receiver;
+        private Tuple(Class<? extends Device> type, int eventType, AbstractPeripheral receiver, DeviceEvent event) {
+            super(type, null);
+            this.eventType = eventType;
+            this.receiver = receiver;
+            this.event = event;
+        }
+    }
 }
--- a/src/share/classes/com/oracle/dio/impl/AbstractPeripheral.java	Thu Jun 18 13:20:08 2015 +0300
+++ b/src/share/classes/com/oracle/dio/impl/AbstractPeripheral.java	Thu Jun 18 13:38:19 2015 +0300
@@ -228,7 +228,7 @@
 
     /* --------- functions to access from EventQueueManager --------------- */
     /* Need to be overriden at childs */
-    protected void processDeviceEvent(DeviceEvent event) {
+    protected void processDeviceEvent(int type, DeviceEvent event) {
     }
 
     protected void processNativeEvent(int type, int... data) {
--- a/src/share/classes/com/oracle/dio/uart/impl/UARTImpl.java	Thu Jun 18 13:20:08 2015 +0300
+++ b/src/share/classes/com/oracle/dio/uart/impl/UARTImpl.java	Thu Jun 18 13:38:19 2015 +0300
@@ -26,14 +26,21 @@
 package com.oracle.dio.uart.impl;
 
 import java.io.*;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
+import java.nio.Buffer;
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.Map;
 import java.util.Objects;
+import java.util.Queue;
 import java.util.StringTokenizer;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.Vector;
 
 import com.oracle.dio.impl.EventQueueManager;
 import com.oracle.dio.power.impl.PowerManagedBase;
@@ -46,7 +53,12 @@
 
 import jdk.dio.*;
 import jdk.dio.uart.*;
+import jdk.dio.uart.UART;
+import jdk.dio.uart.UARTConfig;
 import jdk.dio.uart.UARTConfig.Builder;
+import jdk.dio.uart.UARTEvent;
+import jdk.dio.uart.UARTEventListener;
+import jdk.dio.uart.UARTPermission;
 
 import romizer.*;
 
@@ -56,14 +68,8 @@
     private final Object synchReadLock = new Object();
     private final Object synchWriteLock = new Object();
 
-    private final ByteBuffer writeBuffers[] = new ByteBuffer[2];
-    private final int writeBuffersPositions[] = new int[2];
-
-    private final ByteBuffer readBuffers[] = new ByteBuffer[2];
-    private final int readBuffersPositions[] = new int[2];
-
-    private int readBufferIdx = 0;
-    private int writeBufferIdx = 0;
+    private final Queue<ByteBuffer> writeBuffers = new LinkedList<ByteBuffer>();
+    private final Queue<ByteBuffer> readBuffers = new LinkedList<ByteBuffer>();
 
     private InputRoundListener<UART, ByteBuffer> inRoundListener;
     private OutputRoundListener<UART, ByteBuffer> outRoundListener;
@@ -120,26 +126,33 @@
 
     private class InternalRoundListener implements  InputRoundListener<UART, ByteBuffer> {
 
-            private final int endPos;
+            private final int toRead;
 
-            private InternalRoundListener(int endPos) {
-                this.endPos = endPos;
+            private InternalRoundListener(int toRead) {
+                this.toRead = toRead;
+            }
+
+            private void stop() {
+                synchronized (synchReadLock) {
+                    try {
+                        stopReading(true);
+                    } catch (IOException e) {
+                        // intentionally ignored
+                    }
+                    synchReadLock.notifyAll();
+                }
             }
 
             @Override
             public void inputRoundCompleted(RoundCompletionEvent<UART, ByteBuffer> event) {
-                if (event.getBuffer().position() >= endPos) {
-                    synchronized (synchReadLock) {
-                        synchReadLock.notifyAll();
-                    }
+                if (event.getNumber() >= toRead || !event.getBuffer().hasRemaining()) {
+                    stop();
                 }
             }
 
             @Override
             public void failed(Throwable ex, UART arg1) {
-                synchronized(synchReadLock){
-                    synchReadLock.notifyAll();
-                }
+                stop();
             }
     }
 
@@ -199,133 +212,178 @@
         AccessController.checkPermission(new UARTPermission(getSecurityName(), DevicePermission.POWER_MANAGE));
     }
 
+
+    private int totalBytesRead;
     @Override
     protected void processNativeEvent(int event, int... data) {
-        final int bytesProcessed = data[0];
-        UARTEventListener listener = eventListeners.get(event);
-        if (listener != null){
-            try{
-                UARTEvent uartEvent = new UARTEvent(this, event);
-                listener.eventDispatched(uartEvent);
-            } catch(Throwable e){
-                //do nothing
+        {
+            UARTEventListener listener = eventListeners.get(event);
+            if (listener != null){
+                try{
+                    UARTEvent uartEvent = new UARTEvent(this, event);
+                    listener.eventDispatched(uartEvent);
+                } catch(Throwable e){
+                    //do nothing
+                }
             }
         }
 
         switch(event){
         case UARTEvent.INPUT_DATA_AVAILABLE:
-            if (inRoundListener != null){
-                ByteBuffer buffer = readBuffers[readBufferIdx];
-                if (null == buffer) {
-                    try{
-                        inRoundListener.failed(new Exception("Event processing error. Read buffer is null"), this);
-                    }catch(Exception e){
-                        //do nothing
+            {
+                final InputRoundListener<UART, ByteBuffer> listener = inRoundListener;
+                if (listener != null){
+                    ByteBuffer buffer = readBuffers.peek();
+                    if (null == buffer) {
+                        Logging.reportError("[UART] No buffer is ready for read operation");
+                        return;
                     }
-                    return;
-                }
-                /*
-                    read0 is designed to copy available data from the javacall buffer to java buffer,
-                    because of that no slice() call is necessary, the following is redundand:
+                    /*
+                        read0 is designed to copy available data from the javacall buffer to java buffer,
+                        because of that no slice() call is necessary, the following is redundand:
 
-                    int bytesReaden = read0(buffer.slice());
+                        int bytesReaden = read0(buffer.slice());
 
-                */
-                int tmpPos = buffer.position();
-                int bytesRead = read0(buffer);
+                    */
 
-                shiftBufferPosition(buffer, tmpPos + bytesRead);
+                    final int bytesRead = read0(buffer);
+                    totalBytesRead += bytesRead;
+                    shiftBufferPosition(buffer, buffer.position() + bytesRead);
 
-                if(!buffer.hasRemaining()
-                   || inRoundListener instanceof InternalRoundListener){
-                    RoundCompletionEvent<UART,ByteBuffer> rcEvent =
-                        new RoundCompletionEvent(this, buffer, buffer.position() - readBuffersPositions[readBufferIdx]);
+                    final boolean syncRead = (listener instanceof InternalRoundListener);
+                    if(!buffer.hasRemaining() || syncRead){
 
-                    if (null != readBuffers[1]) {
-                        //2 buffers schema
-                        //switch buffers, than notify user
-                        readBufferIdx = readBufferIdx == 0 ? 1 : 0;
-                        buffer = readBuffers[readBufferIdx];
-                        readBuffersPositions[readBufferIdx] = buffer.position();
+                        RoundCompletionEvent<UART,ByteBuffer> rcEvent =
+                            new RoundCompletionEvent(this, buffer, totalBytesRead);
 
                         //notify user
                         try{
-                            inRoundListener.inputRoundCompleted(rcEvent);
+                            listener.inputRoundCompleted(rcEvent);
                         }catch(Exception e){
                             //do nothing, listener should not throw an exception
+                            Logging.reportWarning(e.toString());
                         }
-                    }else{
-                        //1 buffer
-                        //notify the user first, then keep reading
-                        try{
-                            inRoundListener.inputRoundCompleted(rcEvent);
-                            readBuffersPositions[readBufferIdx] = buffer.position();
-                        }catch(Exception e){
-                            //do nothing, listener should not throw an exception
+
+                        // sync read operates single buffer -> no need to rearrange the queue or reset total read counter
+                        // sync read alsways stops if buffer is full -> no need to resend notification
+                        if (!syncRead) {
+                            totalBytesRead = 0;
+                            // next time use another buffer
+                            readBuffers.remove();
+                            // if not stopped
+                            if (inRoundListener != null) {
+                                if (buffer.hasRemaining()) {
+                                    readBuffers.add(buffer);
+                                } else {
+                                    // post notification
+                                    rcEvent = new RoundCompletionEvent(this, buffer, 0);
+                                    EventQueueManager.getInstance().postEvent(this, UARTEvent.INPUT_DATA_AVAILABLE, rcEvent);
+                                }
+                            }
                         }
-                    }//end of else 1 buffer
+                    }
                 }
             }
             break;
+        case UARTEvent.OUTPUT_BUFFER_EMPTY:
+            {
+                final int bytesProcessed = data[0];
+                final OutputRoundListener<UART, ByteBuffer> listener = outRoundListener;
+                if (listener != null){
+                    ByteBuffer buffer = writeBuffers.poll();
+                    if (null == buffer) {
+                        Logging.reportError("[UART] No buffer is ready for write operation");
+                        return;
+                    }
+                    // this event is sent if buffer is empty only
+                    shiftBufferPosition(buffer, buffer.limit());
+                    RoundCompletionEvent<UART,ByteBuffer> rcEvent = new RoundCompletionEvent(this, buffer, bytesProcessed);
 
-        case UARTEvent.INPUT_BUFFER_OVERRUN:
-            break;
-        case UARTEvent.OUTPUT_BUFFER_EMPTY:
-            if (outRoundListener != null){
-                ByteBuffer buffer = writeBuffers[writeBufferIdx];
-
-                if (null == buffer) {
+                    //notify user
                     try{
-                        outRoundListener.failed(new Exception("Event processing error. Write buffer is null"), this);
+                        listener.outputRoundCompleted(rcEvent);
                     }catch(Exception e){
                         //do nothing, listener should not throw an exception
+                        Logging.reportWarning(e.toString());
                     }
-                    return;
+                    if(isWriting){
+                        if (buffer.hasRemaining()) {
+                            writeBuffers.add(buffer);
+                        } else {
+                            // post notification
+                            rcEvent = new RoundCompletionEvent(this, buffer, 0);
+                            EventQueueManager.getInstance().postEvent(this, UARTEvent.OUTPUT_BUFFER_EMPTY, rcEvent);
+                        }
+                        buffer = writeBuffers.peek();
+                        if (null != buffer){
+                            writeAsynch0(buffer);
+                        }
+                    }
+                }//if (outRoundListener != null)
+            }
+            break;
+        }//switch(event)
+    }
+
+    @Override
+    protected void processDeviceEvent(int eventType, DeviceEvent event) {
+        if (eventType == UARTEvent.OUTPUT_BUFFER_EMPTY) {
+            final OutputRoundListener<UART, ByteBuffer> listener = outRoundListener;
+            if (listener != null) {
+                //notify user
+                try{
+                    listener.outputRoundCompleted((RoundCompletionEvent)event);
+                }catch(Exception e){
+                    //do nothing, listener should not throw an exception
+                    Logging.reportWarning(e.toString());
                 }
-                shiftBufferPosition(buffer, buffer.position() + bytesProcessed);
-
-                if (!buffer.hasRemaining()) {
-                    RoundCompletionEvent<UART,ByteBuffer> rcEvent = new RoundCompletionEvent(this, buffer, buffer.position() - writeBuffersPositions[writeBufferIdx]);
-
-                    if (null != writeBuffers[1]) {
-                        //2 byffers
-                        //switch buffers, than notify user
-                        writeBufferIdx = writeBufferIdx == 0 ? 1 : 0;
-                        buffer = writeBuffers[writeBufferIdx];
-                        //keep writing from the second buffer before user notice
-                        if(isWriting){
-                            if (buffer.hasRemaining()){
-                                writeAsynch0(buffer);
-                            }
+                if (isWriting) {
+                    final ByteBuffer buffer = ((RoundCompletionEvent<UART, ByteBuffer>)event).getBuffer();
+                    if (buffer.hasRemaining()) {
+                        // if no write process is ongoing
+                        if (writeBuffers.peek() == null) {
+                            writeAsynch0(buffer);
                         }
-                        //notify user
-                        try{
-                            outRoundListener.outputRoundCompleted(rcEvent);
-                        }catch(Exception e){
-                            //do nothing, listener should not throw an exception
-                        }
-                    }else{
-                        //1 buffer
-                        //notify user first, then keep writing
-                        try{
-                            outRoundListener.outputRoundCompleted(rcEvent);
-                        }catch(Exception e){
-                            //do nothing, listener should not throw an exception
-                        }
-                        if(isWriting){
-                            if (buffer.hasRemaining()){
-                                writeAsynch0(buffer);
-                            }
-                        }
-                    }//end of else 1 buffer
-                }else{ //buffer has remaining, keep writing
-                    if(isWriting){
-                        writeAsynch0(buffer);
+                        // no sync is required since event processing is single thread operation
+                        writeBuffers.add(buffer);
+                    } else {
+                        // send again
+                        EventQueueManager.getInstance().postEvent(this, UARTEvent.OUTPUT_BUFFER_EMPTY, event);
                     }
                 }
-            }//if (outRoundListener != null)
-            break;
-        }//switch(event)
+            }
+        } else if (eventType == UARTEvent.INPUT_DATA_AVAILABLE) {
+            final InputRoundListener<UART, ByteBuffer> listener = inRoundListener;
+            if (listener != null) {
+                //notify user
+                try{
+                    listener.inputRoundCompleted((RoundCompletionEvent)event);
+                }catch(Exception e){
+                    //do nothing, listener should not throw an exception
+                    Logging.reportWarning(e.toString());
+                }
+                // if not stopped
+                if (inRoundListener != null) {
+                    final ByteBuffer buffer = ((RoundCompletionEvent<UART, ByteBuffer>)event).getBuffer();
+                    if (buffer.hasRemaining()) {
+                        final ByteBuffer tmp = readBuffers.peek();
+                        readBuffers.add(buffer);
+                        if (tmp == null) {
+                            // by contract next INPUT_DATA_AVAILABLE is suppressed until read0->javacall_uart_read is called.
+                            // if previous InputRoundListener doesn't clear provided buffer we may get into situation when
+                            // processNativeEvent->readBuffer.peek() returns null and javacall flag is not cleared by read0.
+                            // to continue receviving we need to read cached data and clear javacall flags
+                            processNativeEvent(UARTEvent.INPUT_DATA_AVAILABLE, 0);
+                        }
+                    } else {
+                        // send again
+                        EventQueueManager.getInstance().postEvent(this, UARTEvent.INPUT_DATA_AVAILABLE, event);
+                    }
+                }
+            }
+        } else {
+            Logging.reportError("UART.processDeviceEvent: unknown event " + eventType);
+        }
     }
 
 
@@ -514,13 +572,12 @@
         checkPowerState();
         synchronized(synchWriteLock) {
             checkWrite();
-            writeBuffers[0] = src1;
-            writeBuffersPositions[0] = src1.position();
-            writeBuffers[1] = src2;
+
+            writeBuffers.add(src1);
             if (null != src2) {
-                writeBuffersPositions[1] = src2.position();
+                writeBuffers.add(src2);
             }
-            writeBufferIdx = 0;
+
             outRoundListener = listener;
             subscribe(UARTEvent.OUTPUT_BUFFER_EMPTY);
             writeAsynch0(src1);
@@ -536,9 +593,9 @@
         checkOpen();
         synchronized(synchWriteLock) {
             if (isWriting){
+                writeBuffers.clear();
                 outRoundListener = null;
                 unsubscribe(UARTEvent.OUTPUT_BUFFER_EMPTY);
-                writeBuffers[0] = writeBuffers[1] = null;
                 stopWriting0();
                 isWriting = false;
             }
@@ -574,13 +631,11 @@
         synchronized(synchReadLock){
             checkRead();
             inRoundListener = listener;
-            readBuffers[0] = src1;
-            readBuffersPositions[0] = src1.position();
-            readBuffers[1] = src2;
+            readBuffers.add(src1);
             if (null != src2) {
-                readBuffersPositions[1] = src2.position();
+                readBuffers.add(src2);
             }
-            readBufferIdx = 0;
+
             /*
                 subscribe calls set_event_listener, in case of INPUT_DATA_AVAILABLE
                 the native function checks if data available in the internal
@@ -596,11 +651,12 @@
         checkOpen();
         synchronized(synchReadLock) {
             if (null != inRoundListener && syncVer == (inRoundListener instanceof InternalRoundListener) ) {
+                readBuffers.clear();
                 inRoundListener = null;
                 unsubscribe(UARTEvent.INPUT_DATA_AVAILABLE);
-                readBuffers[0] = readBuffers[1] = null;
                 // redundant function, remove later
                 stopReading0();
+                synchReadLock.notifyAll();
             }
         }
     }
@@ -671,7 +727,7 @@
                          * exit immediatelly,
                          * else read with timeout
                          */
-                        startReading(dst, new InternalRoundListener((0 != receiveTriggerLevel) ? receiveTriggerLevel + ret : dst.limit()));
+                        startReading(dst, new InternalRoundListener((0 != receiveTriggerLevel) ? receiveTriggerLevel - readRes : dst.remaining()));
                             try{
                                 if(inputTimeout == Integer.MAX_VALUE){
                                     //timeout disabled, wait forever or till the buffer is fullfilled
@@ -682,7 +738,6 @@
                             }catch(InterruptedException iE){
                                 throw new IOException();
                             } finally {
-                                stopReading(true);
                             }
                     }
                 } // if !event thread
@@ -729,13 +784,10 @@
     public void close() throws IOException{
         if (isOpen()) {
             stopWriting();
-            stopReading();
+            // stops EVERY read thread
+            stopReading((inRoundListener instanceof InternalRoundListener));
             super.close();
         }
-        // unblock read()
-        synchronized(synchReadLock) {
-            synchReadLock.notifyAll();
-        }
     }
 
     @Override