|
|
Non-Blocking Execution in PerlXChat is not a threaded application and is event based, as such, running a process will block XChat until the process is returned, and XChat can continue on. Many people will attempt to get around this issue by using threads, but using threads without careful consideration may cause problems within XChat. As an example, the following code may be fairly straight forward, but has unintended consequences. badscript.pl
With this code, running /demoproblem will result in the following output: Use of uninitialized value in pattern match (m//) at /path/to/your/badscript.pl line 18. <@testuser> george This is because the contents of $_[0][1] has been messed with by the thread. While it may work fine in this specific case, imagine if more scripts catch Your Message. The error list may end up being quite long, for a single problem. Additionally, if you attempt to change the context within the thread, you could very easily cause XChat to crash. This solution is not ideal. One Solution - threads and threads::sharedOne solution to this problem is using Perl's threads::shared module in conjunction with XChat's hook_timer. In this scenario, an array is kept with the results that XChat could deal with, as well as where to run these results. As long as all XChat specific code (such as emit_print, command, and get_list) is not done within the thread, XChat should be fine. If you do need access to things found in get_list, consider getting this information first, passing it to the thread, and only using the thread for process that would otherwise block XChat. This code requires using newer versions of the threads and threads::shared modules. The newer modules are found starting in Perl 5.10. noblock-thread-hook_timer.pl
In this case, you can run /noblock multiple times in a row, on different contexts, and it will print the lines in the correct channel. Additionally, the problem listed at the top of the page with the "Use of uninitialized value" will not show up when hooking Your Message. What does the code do? Parameters are passed to deal_with, which then increments a counter for lines that are dealt with. It then calls the thread creation, passing these parameters to the thread, and then decides if a timer is needed to look for results. This is determined by checking if $active is 1, which would happen on the first instance. If $active is more than 1, a hook_timer already would be running (in theory). When a thread completes, it decrements the shared variable. When the timer completes its execution that time, if the value of $active is 0, all of the active threads have completed, and XChat doesn't need to look for anymore commands to send, otherwise it continues. Note that this code is just an example, and fairly specific to the task at hand. Normally code won't need to simply say two lines and go on. The 2 seconds of sleep would normally hang XChat until execution, but XChat is able to handle the sleep, and still allow messages to come in, and input to be added. Instead, running some other code that would block XChat execution (such as waiting for a process in back ticks or making a TCP connection) would be done inside of thread_command. Any command XChat should run (for display, or messaging a user) would be done in $result->[1]. Other solutions - Using CPAN modulesAs XChat is event based, perhaps a more accurate method would be to use CPAN modules such as POE or EV which are actually designed for event based programming. In the solution I gave above, XChat uses hook_timer to check if a message is available every .1 seconds. While this may be satisfactory in many cases, it isn't exactly what is required. Having a module specifically look for an event is much more accurate. |
