3

I have a pretty complex problem. In my current project, I have a GUI written in Java and a computing engine written in C++.

These are displays in Java which access to data in C++, and I have some issues with concurrency.

There are a long story in this code, so I can't just rewrite all (even if I want it occasionnaly :p).

When the engine modify the datas, it acquire a mutex. It's pretty clean from this side.

The problem is GUI. It is Java Swing, and it access the data without any control, from EventDispatchThread, or from any thread, and acquire the c++ mutex (via JNI) for each unitary access to the kernel (which is not good, for performance and data consistancy).

I already refactor it to encapsulate the lock code in Java in a "NativeMutex" which call the native functions lock and unlock from JNI.

I want write a "ReentrantNativeLock", to avoid rewrite all and just add some high-level lock.

But this ReentrantNativeLock must deal with the EventDisplayThread.

I have defined that this lock implementation must avoid that the EDT takes the mutex (by throwing an exception when the lock method is called from EDT), but just return when the lock is already owned by another thread (to deal with SwingUtilities.InvokeAndWait without rewrite all the dirty code of this application)

Conceptually, It's ok because I focus on synchronization between the C++ engine and the JAVA GUI, but it's not safe from the Java-side.

So I want to go further. If I can know which threads are waiting for EDT (which are the threads that have called "InvokeAndWait"), I can implement something safer. I will be able to check if the owner thread is waiting for EDT, and avoid some ununderstandable but probable bugs which will annoy my futur-myself and my coworker.

So, how can I know which threads are waiting for EDT (which are the threads that have called "InvokeAndWait")

(If I described the context, it's because I am open to listen other ideas which could solve my problem... Only if they doesn't imply a lot of rewrite.)

As some comments make me believe that the context isn't well described, I post some code which, I hope, will explicit my problem.

It's a basic Decorator, m_NativeLock is the non-reentrant nativeLock.

public class ReentrantNativeLock implements NativeLock {

  /**
   * Logger
   */
  private static final Logger LOGGER = Logger.getLogger(ReentrantNativeLock.class);

  public ReentrantNativeLock(NativeLock adaptee) {
    m_NativeLock = adaptee;
  }

  public void lock() {
    if (!SwingUtilities.isEventDispatchThread()) {
      m_ReentrantLock.lock();
      if (m_ReentrantLock.getHoldCount() == 1) { // Only the first lock from a thread lock the engine, to avoid deadlock with the same thread
        m_NativeLock.lock();
      }
    }
    else if (m_ReentrantLock.isLocked()) {
      // It's EDT, but some thread has lock the mutex, so it's ok... We assume that the locked thread as called SwingUtilities.invokeAndWait... But if I can check it, it will be better.
      LOGGER.debug("Lock depuis EDT (neutre). Le lock a été verrouillé, l'accès moteur est (à priori) safe", new Exception());
    }
    else {
      // We try to avoid this case, so we throw an exception which will be tracked and avoided before release, if possible
      throw new UnsupportedOperationException("L'EDT ne doit pas locker elle-même le moteur.");
    }
  }

  public boolean tryLock() {
    if (!SwingUtilities.isEventDispatchThread()) {
      boolean result = m_ReentrantLock.tryLock();
      if (result && m_ReentrantLock.getHoldCount() == 1) {
        result = m_NativeLock.tryLock();// Only the first lock from a thread lock the engine, to avoid deadlock with the same thread
        if (!result) {
          m_ReentrantLock.unlock(); // If the trylock on engin fail, we free the lock (I will put it in a try{}finally{} if I valid this solution.
        }
      }
      return result;
    }
    else if (m_ReentrantLock.isLocked()) {
      // It's EDT, but some thread has lock the mutex, so it's ok... We assume that the locked thread as called SwingUtilities.invokeAndWait... But if I can check it, it will be better.
      LOGGER.debug("Lock depuis EDT (neutre). Le lock a été verrouillé, l'accès moteur est (à priori) safe", new Exception());
      return true;
    }
    else {
      // We try to avoid this case, so we throw an exception which will be tracked and avoided before release, if possible
      throw new UnsupportedOperationException("L'EDT ne doit pas locker elle-même le moteur.");
    }
  }

  public void unlock() {
    if (!SwingUtilities.isEventDispatchThread()) {
      if (m_ReentrantLock.getHoldCount() == 1) {
        m_NativeLock.unlock(); 
      }
      m_ReentrantLock.unlock();
    }
    else {
      LOGGER.debug("Unlock depuis EDT (neutre). Le lock a été verrouillé, l'accès moteur est (à priori) safe", new Exception());
    }
  }
  final ReentrantLock m_ReentrantLock = new ReentrantLock();
  final NativeLock m_NativeLock;
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • I don't think you can easily find out which threads are waiting for the EDT, but can't you record that information yourself? – Guillaume Polet Apr 23 '15 at 09:24
  • 1. `If I can know which threads are waiting for EDT (which are the threads that have called "InvokeAndWait"),` then you have to test for false from isEventDispatchThead, otherwise (on true) there must be invokeLater – mKorbel Apr 23 '15 at 09:46
  • @GuillaumePolet I have thought about it. Write a SwingUtilitiesMyApp.invokeAndWait() which records the calling threads until the task has been executed. But it implies to track down every call to invokeAndWait and will generate some difficulties for my coworkers in the futur to understand the code, and find the good method to use in order to avoid breaking things. It's not very kind and I am kind. – Guillaume Fouillet Apr 23 '15 at 09:48
  • 2. But this ReentrantNativeLock must deal with the EventDisplayThread. dosn't make me sence ---> all updates to the already visible Swing GUI must be done on EDT, nothing else, forgot about, there isn't difference between output from Thread, Socket, ScheduledFuture etc to Swing and output from JNI/JNA/etc, just must be done on EDT, nothing else, you job are notifiers, signals – mKorbel Apr 23 '15 at 09:50
  • 3. use embedded database as bridge, instead of hunting for sync, notify, get/set value 4. voting to close as too broad – mKorbel Apr 23 '15 at 09:52
  • 5. problem (excluding sync via embedded database, or better is the standard db engine instaled on accesible port) is that all processes are asynchronous too volatille, is that your goal – mKorbel Apr 23 '15 at 09:56
  • 6. [something more](http://stackoverflow.com/a/6164084/714968) – mKorbel Apr 23 '15 at 09:57
  • @mKorbel, I don't understand in what your comments could help me. I know what is the EDT, and what must be done in EDT. It seems that I have not been clear when I have describe the context of my question. The fact is that some developers have written a lot of code in JAVA which accesses to data that could be updated from a thread in C++ in an application that I have to maintain. These codes are called from different threads, including EDT. – Guillaume Fouillet Apr 23 '15 at 10:31
  • @mKorbel (bis),It leads to some freeze of GUI, and random crashes when modification and access to data aren't synchronized. It's messy. I want a Lock which call the C++ mutex trough JNI and helps me to find and avoid lock called from EDT in the safest and quickest way. I thought to a ReentrantLock to avoid rewrite a lot of code and track down each access to my kernel in the dirty code. Anyway, thanks for your comments. – Guillaume Fouillet Apr 23 '15 at 10:31
  • 1
    Then, the only way I see to solve this issue is to implement your own `EventQueue`, override `postEvent`, check if event is an `InvocationEvent` and check if there is a notifier object on the `INvocationEvent` (has to be done through reflection because it's protected). In such case, record the current thread as waiting for EDT. Then also override `dispatchEvent` and unset the waiting thread as being waiting for the EDT. I'll post some code later if I get some time. – Guillaume Polet Apr 23 '15 at 11:03
  • @GuillaumePolet Yes, your approach seems the most possible, but I don't think that my prime intention requires that sort of borderline solution. I prefer avoid these solutions to improve dirty code, there are limits to irony :p Thanks (that said, if you still want to try... I am curious) – Guillaume Fouillet Apr 23 '15 at 15:32
  • 1. don't play with postEvent (because reseting all event in EDT, and you describe about sync issue, forgot about) use SecondaryLoop for Java=>7, 2. all your ideas described here is asynchronous, batched, again to use db engine for reducing the delay between two signals – mKorbel Apr 23 '15 at 16:58
  • ,It leads to some freeze of GUI, and random crashes when modification and access to data aren't synchronized. == looks like as usage of InvokeAndWait in the case that isEventDispatchThead returns true (bunch of exceptions plus you lost all events queued in EDT) – mKorbel Apr 23 '15 at 17:01
  • So, how can I know which threads are waiting for EDT == I'm doubt in, can you bet.... – mKorbel Apr 23 '15 at 17:13
  • @GuillaumeFouillet Posted an example. It's not that complicated and it doesn't involve lot's of code. I think it's rather safe, generic and friendly for your future co-workers. – Guillaume Polet Apr 23 '15 at 18:07
  • @mKorbel I didn't know what was SecondaryLoop. It's interesting, and it could be useful in further projects, but not relevant here, besides, I work in Java 6 in this application (dev started some years ago). – Guillaume Fouillet Apr 24 '15 at 07:29
  • @mKorbel For the crashes, these aren't the consequences of misusage of InvokeAndWait (we have fixed this kind of issues years ago), but the consequences of access to invalid memory through JNI. When I get an exception, it's generally an easy issue for me :p. One of the purpose of this NativeReentrantLock is to avoid eventual crashes by throwing a exception before, when we avoid locking because we are in the EDT. C++ isn't a good guy when you mess with his memory :p. – Guillaume Fouillet Apr 24 '15 at 07:35
  • be ensure whit follows 1. your code (any events intialized from Swing GUI - e.g. event from JButton) must to redirect anything ti WorkersThread (Runnable#Thread, in emergency case from SwingWorker). 2. your event queue (EDT - So, how can I know which threads are waiting for EDT) never contains fragments, nor waiting event from any previous events (JProfiler), 3. all events must be close() in finally (JProfiler), 4. working code hasn't any weight to Swing GUI, and viece versa, output from this part only to notified EDT (only methods from Swing APIs), by using invokeLater – mKorbel Apr 24 '15 at 07:43
  • 5. all GUIs around us has two layers, working level - back_end and presentations level (UI/GUI/...) front_end (enterprise apps has middletiers) , those layers by default hasn't direct interactions, boths living own separate lifecycle, your decision (programatically) is connect those two layers, safetly, with required amount of events from WorkingThread to GUI, nothing there waiting to signal from another layer (not meaning e.g. cancelations of process by pushing JButton), working thread executing ..., required output by using invokeLater repainting screen, nothing else – mKorbel Apr 24 '15 at 07:51

2 Answers2

2

What you can do is have your own EventQueue that records events to dispatch, from which Thread they are created and if the Thread is waiting for the event to be dispatched (so, in case a Thread invokes invokeAndWait).

First, push your own queue:

  ThreadTrackingEventQueue queue = new ThreadTrackingEventQueue();
        Toolkit.getDefaultToolkit().getSystemEventQueue().push(queue);

In your implementation of the queue:

  • override postEvent, check if it's an InvocationEvent and if it's waiting to be notified. In such case track the Thread and the corresponding event
  • override dispatchEvent to unmark the calling thread as waiting for the EDT.

Complete example (watch out, it sleeps on the EDT to make collisions happen, but it should never be done in an application):

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InvocationEvent;
import java.lang.reflect.Field;
import java.util.Hashtable;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class TestEventQueue {

    private final ThreadTrackingEventQueue queue;

    public static class ThreadTrackingEventQueue extends EventQueue {

        private Field notifierField;
        private Hashtable<AWTEvent, Thread> waitingThreads = new Hashtable<AWTEvent, Thread>();

        public ThreadTrackingEventQueue() throws NoSuchFieldException, SecurityException {
            notifierField = InvocationEvent.class.getDeclaredField("notifier");
            notifierField.setAccessible(true);
        }

        @Override
        public void postEvent(AWTEvent event) {
            if (!SwingUtilities.isEventDispatchThread() && event.getClass() == InvocationEvent.class) {
                try {
                    Object object = notifierField.get(event);
                    if (object != null) {
                        // This thread is waiting to be notified: record it
                        waitingThreads.put(event, Thread.currentThread());
                    }
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            super.postEvent(event);
        }

        @Override
        protected void dispatchEvent(AWTEvent event) {
            try {
                super.dispatchEvent(event);
            } finally {
                if (event.getClass() == InvocationEvent.class) {

                    waitingThreads.remove(event);
                }
            }

        }

        public Hashtable<AWTEvent, Thread> getWaitingThreads() {
            return waitingThreads;
        }
    }

    public TestEventQueue(ThreadTrackingEventQueue queue) {
        this.queue = queue;
    }

    private void initUI() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JTextArea textArea = new JTextArea(30, 80);
        JButton button = new JButton("Start");
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    start();
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        });
        frame.add(new JScrollPane(textArea));
        frame.add(button, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
        Timer t = new Timer(100, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                Hashtable<AWTEvent, Thread> waitingThreads = (Hashtable<AWTEvent, Thread>) queue.getWaitingThreads().clone();
                if (waitingThreads.size() > 0) {
                    for (Thread t : queue.getWaitingThreads().values()) {
                        textArea.append("Thread " + t.getName() + " is waiting for EDT\n");
                    }
                } else {
                    textArea.append("No threads are waiting\n");
                }
            }
        });
        t.start();
    }

    protected void start() throws InterruptedException {
        final Random random = new Random();
        ExecutorService pool = Executors.newFixedThreadPool(50);
        for (int i = 0; i < 50; i++) {
            pool.submit(new Callable<Boolean>() {
                @Override
                public Boolean call() throws Exception {
                    System.out.println("sleeping before invoke and wait");
                    Thread.sleep(random.nextInt(2000) + 200);
                    System.out.println("invoke and wait");
                    SwingUtilities.invokeAndWait(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                System.out.println("sleeping on EDT, bwark :-(");
                                // Very very bad, but trying to make collisions
                                // happen
                                Thread.sleep(random.nextInt(200) + 100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                    return true;
                }
            });
        }
        System.out.println("Invoked all");
    }

    public static void main(String[] args) throws NoSuchFieldException, SecurityException {
        final ThreadTrackingEventQueue queue = new ThreadTrackingEventQueue();
        Toolkit.getDefaultToolkit().getSystemEventQueue().push(queue);
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    TestEventQueue test = new TestEventQueue(queue);
                    test.initUI();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • Hum... It seems fine. I will think about it, and perhaps test it. – Guillaume Fouillet Apr 24 '15 at 07:55
  • @Guillaume Fouillet see usage of isEventDispatchThread in this answer, EDT returns false in alll cases when is empty, (good optimalized app) this can be 5seconds, 20-30 after masive repainting is done, you cant trace that, empty EDT always returns false, this is desired goal for your app (all mouse or key event alive this queue) – mKorbel Apr 24 '15 at 07:56
  • @Guillaume Fouillet [for testing purposes](http://stackoverflow.com/a/10770381/714968) – mKorbel Apr 24 '15 at 08:02
  • Thinking about it, I wonder if it would not be more efficient to stack Threads on a queue (or null if `notifier` is null), and put those with non-null `notifier` in a HashSet to check quickly which threads are waiting for EDT. Efficiency depends on the hashcode implementation for Thread (which seems to be a native implementation and could always return 0). Moreover, the `dispatchEvent` will be executed faster (no need to find the Event in a map, just deque something). – Guillaume Fouillet Apr 24 '15 at 10:06
  • @GuillaumeFouillet Be careful of high concurrency here. Afte dispatchEvent has been called, your invoking Thread gets notified and could already have re-submitted a new event. There are probably more efficient ways to do this, but watch our for concurrency. – Guillaume Polet Apr 24 '15 at 12:28
1

You wrote:

The fact is that some developers have written a lot of code in JAVA which accesses to data that could be updated from a thread in C++ in an application that I have to maintain. These codes are called from different threads, including EDT.

The problem is the EDT that accesses the data. You may need to make some changes to the written code so that the EDT never directly manipulates shared data. This means that the EDT must give data-related tasks to some other threads:

  • If the EDT need to change some data, it creates a new thread to do the job.

  • If a thread need to update changes to the GUI, it calls either InvokeLater() or InvokeAndWait().

---------- My Answer (Second Edition) ----------

Hey, there is still some lights at the end of the path.

  1. Let's refactor all the code to make sure there is only one InvokeAndWait() at a time. How to do that? First you need to write a new global method called MyInvokeAndWait(). This method use a lock to ensure that only one thread at a time can call InvokeAndWait(). Then use an IDE to search for all InvokeAndWait() and replace them with MyInvokeAndWait().

  2. Now, inside MyInvokeAndWait(), make sure that when InvokeAndWait() is called, an atomic variable threadId is set to the id of the calling thread (note that the invocation of InvokeAndWait() will block the calling thread). When InvokeAndWait() is finished, the threadId is cleared.

  3. This way, whenever the EDT accesses the data, you can check whether the owner thread has the same id with threadId. If this is the case, let EDT do its job, otherwise throw an exception.

Well... You don't need to ensure only one thread at a time can call InvokeAndWait(). You can add all calling thread id(s) to a collection and then verify that the owner thread id is in the collection.

Huy
  • 46
  • 5
  • I dont see how this could be of any help to him. As he says, he understants what the problem is but wishes for a way to detect wheter a Thread is waiting on EDT or not. – Renatols Apr 23 '15 at 12:32
  • Thanks for that answer. I know that. In fact, When it's possible without major refactoring, I already refactored the code with a StringWorker in order to put the stuff where they belong. But I haven't the time to read every single line of code which could be accessed from EDT. So I have implemented this ReentrantNativeLock in order to monitor access to the engine. It let me known who try to lock it, logging those who aren't allowed to, and take some garantee with those who are allowed. I will post the code in minutes. – Guillaume Fouillet Apr 23 '15 at 13:15
  • The problem is the EDT that accesses the data == EDT is queue where methods implemented in AWT/Swing pushed arrays of events – mKorbel Apr 23 '15 at 17:04
  • You may need to make some changes to the written code so that the EDT never directly manipulates shared data. == EDT is queue where methods implemented in AWT/Swing pushed arrays of events that are flushed and repainting all changes in the one moment, – mKorbel Apr 23 '15 at 17:05
  • This means that the EDT must give data-related tasks to some other threads: == EDT is designated just to repaint all changes in AWT/Swing (J)Components to the GPU, disadvantage is that this process isn't stepped, batched, logically managable – mKorbel Apr 23 '15 at 17:07
  • Nice try, but I think that this solution will seriously decrease the performances of the application, and increase the latence for user by limiting the InvokeAndWait to one thread. I though about this kind of solution, creating a MySwingUtilities class which overrides InvokeAndWait, that just enqueue and dequeue the threads which wait for an posted event. But this solution implies a lot of refactoring (my application has more than hundred of modules). – Guillaume Fouillet Apr 24 '15 at 07:44