On Thu, 20 Feb 2025 22:35:07 GMT, Sunmisc Unsafe <d...@openjdk.org> wrote:
>> (Copied from https://bugs.openjdk.org/browse/JDK-8319447) >> >> The problems addressed by this CR/PR are that ScheduledThreadPoolExecutor is >> both ill-suited for many (if not most) of its applications, and is a >> performance bottleneck (as seen especially in Loom and CompletableFuture >> usages). After considering many options over the years, the approach taken >> here is to connect (lazily, only if used) a form of ScheduledExecutorService >> (DelayScheduler) to any ForkJoinPool (including the commonPool), which can >> then use more efficient and scalable techniques to request and trigger >> delayed actions, periodic actions, and cancellations, as well as coordinate >> shutdown and termination mechanics (see the internal documentation in >> DelayScheduler.java for algotihmic details). This speeds up some Loom >> operations by almost an order of magnitude (and similarly for >> CompletableFuture). Further incremental improvements may be possible, but >> delay scheduling overhead is now unlikely to be a common performance concern. >> >> We also introduce method submitWithTimeout to schedule a timeout that >> cancels or otherwise completes a submitted task that takes too long. Support >> for this very common usage was missing from the ScheduledExecutorService >> API, and workarounds that users have tried are wasteful, often leaky, and >> error-prone. This cannot be added to the ScheduledExecutorService interface >> because it relies on ForkJoinTask methods (such as completeExceptionally) to >> be available in user-supplied timeout actions. The need to allow a pluggable >> handler reflects experience with the similar CompletableFuture.orTimeout, >> which users have found not to be flexible enough, so might be subject of >> future improvements. >> >> A DelayScheduler is optionally (on first use of a scheduling method) >> constructed and started as part of a ForkJoinPool, not any other kind of >> ExecutorService. It doesn't make sense to do so with the other j.u.c pool >> implementation ThreadPoolExecutor. ScheduledThreadPoolExecutor already >> extends it in incompatible ways (which is why we can't just improve or >> replace STPE internals). However, as discussed in internal documentation, >> the implementation isolates calls and callbacks in a way that could be >> extracted out into (package-private) interfaces if another j.u.c pool type >> is introduced. >> >> Only one of the policy controls in ScheduledThreadPoolExecutor applies to >> ForkJoinPools with DelaySchedulers: new method cancelDelayedTasksOnShutdown >> controls whether quiescent shutdown sh... > > Is it possible to use getAndSet instead of cas-loop on #pend? > > final Task t = tail.getAndSet(task); > t.next = task; > > but that would require a new head field to bypass, probably not worth the > gamble if the footprint increases a lot > @sunmisc You are right that it would be nice if there were a way to > efficiently use getAndSet here because a failed reference CAS hits slow paths > that vary across GCs. But all of the ways I know to do this are much worse. After a few days of benchmarks, I realized that you would be absolutely right. Although I thought if we separate the head (for deleting) and the tail (for inserting) there would be less contention. Even the fact that we can change the head (delete) only in one thread does not help us from volatile, moreover, it became worse. Perhaps I have made a mistake somewhere in the implementation ------------- PR Comment: https://git.openjdk.org/jdk/pull/23702#issuecomment-2683105690