LeVanPhuUIT / jnativehook

Automatically exported from code.google.com/p/jnativehook
0 stars 0 forks source link

registerNativeHook() causes 100% CPU usage #37

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?
1. Call GlobalScreen.registerNativeHook(); in the Java program.
2. Watch CPU utilization in the System Monitor jump to 100% (included with KDE 
on Linux)

What is the expected output? What do you see instead?
The native hook should not consume 100% of my processor.
I know the System Monitor is accurate because my laptop fan turns on soon after 
the program starts, which means the processor is maxed out.

What version of the product are you using? On what operating system?
Version 1.1.3, on Linux Mint 14

Please provide any additional information below.
Commenting out GlobalScreen.registerNativeHook(); returns the CPU to normal 
usage (5%).

Original issue reported on code.google.com by postmark...@gmail.com on 5 Feb 2013 at 5:58

GoogleCodeExporter commented 8 years ago
Default Java version with Linux Mint 14:

$java -version
java version "1.7.0_09"
OpenJDK Runtime Environment (IcedTea7 2.3.4) (7u9-2.3.4-0ubuntu1.12.10.1)
OpenJDK 64-Bit Server VM (build 23.2-b09, mixed mode)

Original comment by postmark...@gmail.com on 5 Feb 2013 at 6:03

GoogleCodeExporter commented 8 years ago
Please unchecked all of the boxes in the demo application and check to see if 
the CPU utilization is still high.  Let me know how that works.

Original comment by a...@1stleg.com on 5 Feb 2013 at 6:32

GoogleCodeExporter commented 8 years ago
I unchecked all four boxes on the demo, and the processor was still at 100%.  
This is consistent with what I saw in my application.  If I comment out 
addNativeKeyListener(), etc, there is no change.  If I comment out 
registerNativeHook(), the processor utilization returns to normal.

Original comment by postmark...@gmail.com on 5 Feb 2013 at 2:47

GoogleCodeExporter commented 8 years ago
I peeked at the source, and see a possible cause.  Please see my explanation 
that follows.

From NativeThread.c

// Async requires that we loop so that our thread does not return.
if (XRecordEnableContextAsync(disp_data, context, LowLevelProc, NULL) != 0) {
    // Set the exit status.
    *status = RETURN_SUCCESS;

    while (running) {
        XRecordProcessReplies(disp_data);
    }
    XRecordDisableContext(disp_ctrl, context);
}

The documentation for XRecordEnableContextAsync says:
"Each time it reads data from the server connection, Xlib will check for 
incoming replies and call callback as necessary. The application may direct 
Xlib explicitly to check for Record data with the XRecordProcessReplies 
function."

Therefore it seems like the XRecordProcessReplies call is not needed, except in 
cases such as: the applicagtion is shutting down, so you want to be sure the 
Xlib event queue is empty.

I understand that you need to loop to keep the thread from exiting (because 
XRecordEnableContextAsync does not block).  The friendly and easy way to do 
this would be:

    while (running) {
        //XRecordProcessReplies(disp_data);  //optional, I think?
        sleep(1);  // this prevents 100% processor utilization
    }

Please let me know what you think.  In general, these types of loops cause 100% 
processor utilization, so I have a feeling the sleep() call will fix the 
problem.

Thanks for your help.

Original comment by postmark...@gmail.com on 5 Feb 2013 at 3:43

GoogleCodeExporter commented 8 years ago
I compiled with this change, and it solved the problem.  Events are detected 
more slowly now (because of the sleep), but for my application, that's ok.

In src/native/unix/NativeThread.c

    while (running) {
        XRecordProcessReplies(disp_data);
        sleep(1);  // this prevents 100% processor utilization
    }

I think most users will want better performance, so I wouldn't mark this as 
solved yet.  

Is there a reason why XRECORD_ASYNC is defined?  Can we use the synchronous 
method instead?

Thanks.

Original comment by postmark...@gmail.com on 5 Feb 2013 at 4:32

GoogleCodeExporter commented 8 years ago
The reason XRECORD_ASYNC is defined is because the sync method of is broken 
upstream.  I will do some testing and see if I can figure out a solution.

Original comment by a...@1stleg.com on 5 Feb 2013 at 7:49

GoogleCodeExporter commented 8 years ago
So there doesn't appear to be a "good" solution to this problem.  The reason 
ASYNC is defined is outlined here 
https://bugs.freedesktop.org/show_bug.cgi?id=42356  The short story is that you 
cant stop it once it starts.

There are a few possible solutions:

Use pthread_yield or sched_yield.  These appear pause when needed but also lead 
to terrible performance.

Use sleep(1).  This works fine but guarantees terrible performance.

Use usleep or nanosleep to pause execution for ~100MS.  Will cause a short 
delay in event delivery but will keep the cpu from burning.  Drawback is that 
it requires _POSIX_C_SOURCE >= 199309L.

You could also take a look at the sample code attached to the XRecord bug 
report.  I may have made a mistake in there due to some of the assumptions I 
made that were not covered in the documentation.

The usleep or nanosleep method is probably the best approach until the bug is 
fixed upstream and ASYNC can be undefined.  I will add a simple implementation 
to the SVN branch for 1.1 shortly.

Original comment by a...@1stleg.com on 6 Feb 2013 at 6:18

GoogleCodeExporter commented 8 years ago
Small update, using XSync(disp_data, false) instead of sleep(1) seems to 
produce good results.  Give it a try and let me know if you notice and adverse 
effects particularly with registering and then un-registering the native hook.  
Changes are available in the 1.1 branch.

http://jnativehook.googlecode.com/svn/branches/1.1/

Original comment by a...@1stleg.com on 6 Feb 2013 at 7:08

GoogleCodeExporter commented 8 years ago
The XSync update is a partial success.  There are still several problems:

1) When I close the demo window, the program doesn't quit.  I have to use 
Ctrl-C in the console.  The sleep version quits normally when I close the GUI.
2) When I'm mousing, the processor jumps to 40%.  The sleep version is at 15%.
3) When I'm typing, the processor jumps to 20%.  The sleep version is at 10%.

When I'm not typing or mousing, the usage is about the same between the two 
versions.  I also didn't have any problems registering and unregistering the 
listeners (using the four checkboxes on the GUI).

Original comment by postmark...@gmail.com on 6 Feb 2013 at 2:43

GoogleCodeExporter commented 8 years ago
I have committed the nanosleep implementation for the time being.  That looks 
like it prevents the 100% CPU at idel without any side effects.  The CPU still 
seems to spike in the X process when events are received.  Aside from the 
XRecord processing, the LowLevelProc method is the one being called each time 
an event is received.  I tried commenting out the entire method and it seems to 
supports my assertion that CPU is being eaten up by the XRecord extension.  I 
am going to leave this bug open until the upstream bug(s) are addressed.  If 
you figure out any additional information please post it here.

Original comment by a...@1stleg.com on 6 Feb 2013 at 6:55

GoogleCodeExporter commented 8 years ago
Mark, could you please share a .jar that you built after the change you 
mentioned in #5 above? 
We are facing these issues on linux systems after we integrated jnativehook 
library in our in-house application. We need the version which avoids 100% CPU 
utilization on linux and slow detection of events is Ok with our app. Thanks.

Original comment by avibham...@gmail.com on 7 Feb 2013 at 7:41

GoogleCodeExporter commented 8 years ago
Here is the jar with the nanosleep workaround.  CPU at idle is low, however, it 
does spike when events are received.

I am looking into the upstream XRecord issue to see if I can figure out a 
better workaround.

Original comment by a...@1stleg.com on 7 Feb 2013 at 7:48

Attachments:

GoogleCodeExporter commented 8 years ago
Here is the jar I built with the 1 second delay, as described in #5 above.

Original comment by postmark...@gmail.com on 8 Feb 2013 at 3:20

Attachments:

GoogleCodeExporter commented 8 years ago
Small status update for those of you watching this bug.  I have some test code 
on my system that appears to work around the XRecordEnableContext (non-async) 
not stopping.  The problem is it's a seriously ugly hack that will need some 
testing after integration and it will defiantly cause issues if fixed upstream, 
however it does work for the current version of XRecord.  I maybe able to add 
some form of dynamic selection to work around the issue but I am not sure if 
its worth the effort as it doesn't seem to produce a whole lot better CPU usage 
than the current nanosleep workaround.  I am still exploring a couple of other 
possibilities including using some combination of pthread_kill() + sigaction() 
or pthread_cancel() + pthread_cleanup_push/_pop().  

Original comment by a...@1stleg.com on 10 Feb 2013 at 1:38

GoogleCodeExporter commented 8 years ago
I seem to have figured out a way to get the sync version of XRecord work as of 
revision 694 (http://jnativehook.googlecode.com/svn/branches/1.1/).  Please 
test and let me know if it works for you.

Original comment by a...@1stleg.com on 7 Mar 2013 at 12:51

GoogleCodeExporter commented 8 years ago
This should be solved at this point.  I will release 1.1.4 shortly.

Original comment by a...@1stleg.com on 11 Mar 2013 at 11:16

GoogleCodeExporter commented 8 years ago

Original comment by a...@1stleg.com on 11 Mar 2013 at 11:16

GoogleCodeExporter commented 8 years ago
Great work. Looking forward to next release specially with fixes for #37 and 
#40.

Original comment by avibham...@gmail.com on 12 Mar 2013 at 6:27