2

I am using inputbot to write a program that provides some global macros for my computer. For example, when I press the h key, it should execute the macro typing

Hello World

into the current application. I tried to implement it like this:

extern crate inputbot;

fn main() {
    let mut callback = || {
        inputbot::KeySequence("Hello World").send();
    };

    inputbot::KeybdKey::HKey.bind(callback);

    inputbot::handle_input_events();
}

However, when I pressed the h key what I actually got was:

hHHHHHHHHHHHHHHHHHHHHHHHHHEHEHhEhEEHHhEhEhEHhEHHEHHEEHhEHlhEHEHHEHLEHLHeeleleelelelllelelleelehlhehlleeheehelheelleeleelhllllllellelolelellelleoleloloelellololol olollollelllolllol lloo ol o oo l lo lolooloooloo loo LOWOLO O L OLW WOWO L WLLOLOW L O O O O o WOWW low o oOow WWW WOW wowooWWWO oOWRWOoor W RoW oOWorororWRRWLR rLROwoRWLWOworo WorrrRWl ow o WRLR OLw o OWLDol rollWWLDWowDLlroWWo r oWDWOL dorRrwrolrdrrorlrLWDRdodRLowdllrllolrdlrddolrdlrldowldorowlrdlrorloLDLWDLoddlrddlrdldldldrrdordldrlrddrodlrrldoldlrlddldlrdlldlrdlddrlddldddlddlddd

The macro was triggering itself each time it sent the h key event.

How can I prevent a Fn from being invoked again while another instance of it is still running? This is the main functionality of a small application, so there's nothing else to really worry about compatibility with.


My naive attempt to fix this was to add a mut running variable in main, which callback would set to true while it was running, or immediately return if it was already true:

extern crate inputbot;

use std::time::Duration;
use std::thread::sleep;

fn main() {
    let mut running = false;
    let mut callback = || {
        if running { return };
        running = true;

        inputbot::KeySequence("Hello World").send();

        // wait to make sure keyboard events are done. 
        sleep(Duration::from_millis(125));

        running = false;
    };

    inputbot::KeybdKey::HKey.bind(callback);
    inputbot::handle_input_events();
}

However, this doesn't compile:

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`

After some reading, my understanding is now that a Fn closure (required by inputbot's .bind() methods) can't own any mutable data, like a captured mut variable.

Maybe it's possible to wrap the variable in some kind of non-mut value? Perhaps some kind-of lock, to make the potential concurrency safe, like this pseudocde?

fn main() {
    let mut running = false;
    let lockedRunning = example::Lock(&running);
    let mut callback = || {
        {
            let mut running = lockedRunning.acquire();
            if running { return };
            running = true;
        }

        inputbot::KeySequence("Hello World").send();

        // wait to make sure keyboard events are done. 
        sleep(Duration::from_millis(125));

        {
            let mut running = lockedRunning.acquire();
            running = false;
        }
    };
}
Handles
  • 23
  • 2
  • 1
    Are you sure there's a guarantee that the callback is called immediately? If not, a lock might not actually be what you want, since the callback triggered by the first callback may be called after the first callback returned, so it would be able to grab the lock and trigger another round of key strokes – Sven Marnach Jan 25 '18 at 14:52
  • @SvenMarnach Good point, and I don't think there is such a guarantee from `inputbot`. – Handles Jan 25 '18 at 21:58

2 Answers2

5

What you want here is that the function is mutually exclusive to itself.

Rust allows you to do this with the Mutex struct. It allows you to hold a lock that when acquired stops anyone else from taking it until you release it.

Specifically the functionality you want is the try_lock method which would allow you to check if the lock has already been acquired and would allow you to handle that case.

let lock = mutex.try_lock();

match lock {
    Ok(_) => {
       // We are the sole owners here
    }
    Err(TryLockError::WouldBlock) => return,
    Err(TryLockError::Poisoned(_)) => {
        println!("The mutex is poisoned");
        return
    }
}
red75prime
  • 3,733
  • 1
  • 16
  • 22
Neikos
  • 1,840
  • 1
  • 22
  • 35
  • 1
    "We have poisoned the mutex" was a bit ambiguous. The mutex wasn't actually poisoned by `try_lock`. – red75prime Jan 25 '18 at 08:36
  • 1
    Would a `RefCell` be sufficient here? It's unclear to me whether there are threads at play or not. – Matthieu M. Jan 25 '18 at 08:58
  • You are right that could be, I am wondering though how one would get interleaving then instead of just an infinite loop. – Neikos Jan 25 '18 at 09:20
  • @MatthieuM. the question states that the closure works as a callback, the library can call it from different threads. – red75prime Jan 25 '18 at 12:22
  • @red75prime: I didn't know this particular library, so didn't know if it was multi-threaded or not :) – Matthieu M. Jan 25 '18 at 13:41
5

Using an atomic value is a bit simpler than a Mutex as you don't need to worry about failure cases and it can easily be made into a static variable without using lazy-static:

use std::sync::atomic::{AtomicBool, Ordering};

fn main() {
    let is_being_called = AtomicBool::new(false);

    bind(move || {
        if !is_being_called.compare_and_swap(false, true, Ordering::SeqCst) {
            print!("I'm doing work");
            is_being_called.store(false, Ordering::SeqCst);
        }
    });
}

I have a hunch that this is also more efficient than using a Mutex as no heap allocations need to be made, but I didn't benchmark it.

If you are in a single-threaded context and your callback is somehow (accidentally?) recursive (which closures cannot be) you can also use a Cell:

use std::cell::Cell;

fn main() {
    let is_being_called = Cell::new(false);

    bind(move || {
        if !is_being_called.get() {
            is_being_called.set(true);
            print!("doing work");
            is_being_called.set(false);
        }
    })
}

If you happen to have a FnMut closure, you don't even need the Cell and can just use a boolean:

fn main() {
    let mut is_being_called = false;

    bind(move || {
        if !is_being_called {
            is_being_called = true;
            print!("doing work");
            is_being_called = false;
        }
    })
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    How is this simpler than `try_lock`? The main difference I see is that you turn poisoning into a deadlock. – CodesInChaos Jan 25 '18 at 15:06
  • @CodesInChaos do you have a suggestion how I can improve my sentence where I state my rationale for saying simpler: "as you don't need to worry about certain failure cases and it can easily be made into a static variable"? I'm also missing where you see a deadlock... could you expand on that some more? – Shepmaster Jan 25 '18 at 15:10
  • Which failure cases do you not have to worry about? The only failure case I can think of is if the code inside the lock panics, in which case unwrapping `try_lock` crashes with a poisoned mutex panic (easy to debug) while yours hangs forever on the next attempt to acquire the lock. – CodesInChaos Jan 25 '18 at 15:11
  • 1
    @CodesInChaos literally the fact that there is no `Result` whatsoever in the first code example — it *cannot* fail. Why would it hang forever? If the block with `println!("recur");` executes, the atomic variable has been set to `true`; all future attempts to read the variable would return `true` and the function wouldn't execute. I'm not sure what you mean by "acquire the lock"; AFAIK that's not how atomic variables work. – Shepmaster Jan 25 '18 at 15:12
  • I misread it as `while`. But `if` still has a similar effect, *silently* preventing this code from ever running again after a panic. (By acquiring the lock, I mean your CAS instruction, which corresponds to the `try_lock` operation of a simple spinlock) – CodesInChaos Jan 25 '18 at 15:17
  • @CodesInChaos that is true, but *if* something catches the panic from this code, it should be reporting it in some fashion, negating the "silent" part. That code could also be responsible for resetting `IS_CALLING_FOREVER` if it's reasonable within the application. – Shepmaster Jan 25 '18 at 15:22
  • @Shepmaster I _think_ the AtomicBool should be createable inside main and just passed to the closure in this specific instance, and that might make a clearer solution. Do you think that would warrant a separate answer to this question, or would it be better to add a snippet to the end of this answer? IMO this is a good general solution, but the OP also has the specific use case of using inputbot. – daboross Jan 25 '18 at 19:38
  • @daboross you are right that it doesn't really matter where you put the `AtomicBool` so long as it is in scope of where it is needed. I don't follow your point about the specifics of inputbot — You can call `inputbot::KeybdKey::HKey.bind(forever);` the same as you would `inputbot::KeybdKey::HKey.bind(callback);`. I only used a function so I could make it recursive, as I explained in the final paragraph. Do you think that's not sufficiently clear and we need to show another form? – Shepmaster Jan 25 '18 at 19:50
  • I guess not. My only worry was that the answer might imply static variables are required when using `AtomicBool`, while `let x = AtomicBool::new(false);` should work just as well for OP's use case. – daboross Jan 25 '18 at 19:53