com.arangodb.shaded.vertx.core.net.impl.pool.CombinerExecutor Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package com.arangodb.shaded.vertx.core.net.impl.pool;
import com.arangodb.shaded.netty.util.internal.PlatformDependent;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Lock free executor.
*
* When a thread submits an action, it will enqueue the action to execute and then try to acquire
* a lock. When the lock is acquired it will execute all the tasks in the queue until empty and then
* release the lock.
*/
public class CombinerExecutor implements Executor {
private final Queue> q = PlatformDependent.newMpscQueue();
private final AtomicInteger s = new AtomicInteger();
private final S state;
private final ThreadLocal current = new ThreadLocal<>();
public CombinerExecutor(S state) {
this.state = state;
}
@Override
public void submit(Action action) {
q.add(action);
if (s.get() != 0 || !s.compareAndSet(0, 1)) {
return;
}
Task head = null;
do {
try {
head = pollAndExecute(head);
} finally {
s.set(0);
}
} while (!q.isEmpty() && s.compareAndSet(0, 1));
if (head != null) {
Task inProgress = current.get();
if (inProgress == null) {
current.set(head);
try {
while (head != null) {
head.run();
head = head.next;
}
} finally {
current.remove();
}
} else {
merge(inProgress, head);
}
}
}
private Task pollAndExecute(Task head) {
Action action;
while ((action = q.poll()) != null) {
Task task = action.execute(state);
if (task != null) {
if (head == null) {
head = task;
} else {
merge(head, task);
}
}
}
return head;
}
private static void merge(Task head, Task tail) {
Task tmp = tail.prev;
tail.prev = head.prev;
head.prev.next = tail;
head.prev = tmp;
}
}