All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.basestar.event.DefaultPump Maven / Gradle / Ivy

There is a newer version: 1.2.2
Show newest version
package io.basestar.event;

/*-
 * #%L
 * basestar-event
 * %%
 * Copyright (C) 2019 - 2020 Basestar.IO
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Metrics;
import lombok.extern.slf4j.Slf4j;

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
public class DefaultPump implements Pump {

    private static final int INITIAL_DELAY_MILLIS = 500;

    private static final int MIN_DELAY_MILLIS = 1;

    private static final int MAX_DELAY_MILLIS = 500;

    private static final int SHUTDOWN_WAIT_SECONDS = 5;

    private final Receiver receiver;

    private final Handler handler;

    private final int minThreads;

    private final int maxThreads;

    private final ScheduledExecutorService executorService;

    private final Counter total = Metrics.counter("events.pump.total");

    private final Object lock = new Object();

    @SuppressWarnings("all")
    private final Random random = new Random();

    private volatile int count;

    public DefaultPump(final Receiver receiver, final Handler handler, final int minThreads, final int maxThreads) {

        this.receiver = receiver;
        this.handler = handler;
        this.minThreads = minThreads;
        this.maxThreads = maxThreads;
        this.executorService = Executors.newScheduledThreadPool(minThreads);
        Metrics.gauge("events.pump.threads", this, t -> t.count);
    }

    @Override
    public void start() {

        for(int i = 0; i != minThreads; ++i) {
            another(INITIAL_DELAY_MILLIS + delay());
        }
    }

    @Override
    public void flush() {

        while(true) {
            final Integer results = receiver.receive(handler).join();
            assert results != null;
            if(results == 0) {
                return;
            }
        }
    }

    private void another() {

        another(delay());
    }

    private void another(long delay) {

        synchronized (lock) {
            if (count < maxThreads) {
                ++count;
                executorService.schedule(() -> {
                    while (true) {
                        try {
                            final Integer results = receiver.receive(handler).join();
                            assert results != null;
                            total.increment(results);
                            synchronized (lock) {
                                if (Thread.interrupted()) {
                                    --count;
                                    return;
                                }
                                if (results == 0) {
                                    if (count > minThreads) {
                                        --count;
                                        return;
                                    }
                                } else {
                                    another();
                                }
                            }
                        } catch (final Throwable e) {
                            log.error("Uncaught in event pump", e);
                        }
                    }
                }, delay, TimeUnit.MILLISECONDS);
            }
        }
    }

    private long delay() {

        return (long)MIN_DELAY_MILLIS + (long)random.nextInt(MAX_DELAY_MILLIS - MIN_DELAY_MILLIS);
    }

    @Override
    public void stop() {

        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(SHUTDOWN_WAIT_SECONDS, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
                if (!executorService.awaitTermination(SHUTDOWN_WAIT_SECONDS, TimeUnit.SECONDS)) {
                    log.error("Failed to shut down executor service");
                }
            }
        } catch (final InterruptedException e) {
            executorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy