class Awaiter {

    private final Logger logger;
    private final Semaphore semaphore = new Semaphore(1);
    private final AtomicBoolean waiting = new AtomicBoolean(false);
    private volatile boolean haveWork;

    public Awaiter(Logger logger) {
        this.logger = logger;
    }

    public void doAwait(long timeout) {<FILL_FUNCTION_BODY>}

    public void wakeUp() {
        haveWork = true;
        if (waiting.compareAndSet(true, false)) {
            semaphore.release(1);
        }
    }

    public void wakeUp(boolean force) {
        haveWork = true;
        if (force) {
            waiting.set(false);
            semaphore.release(1);
        } else {
            if (waiting.compareAndSet(true, false)) {
                semaphore.release(1);
            }
        }
    }
}

        if (waiting.compareAndSet(false, true)) {
            if (haveWork) {
                haveWork = false;
            } else {
                try {
                    semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS);
                    semaphore.drainPermits();
                } catch (Exception e) {
                    logger.warn("Semaphore tryAcquire exception", e);
                }
            }
            waiting.set(false);
        }
    
<no_super_class>