| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| package org.apache.felix.eventadmin.impl.tasks; |
| |
| import java.util.Collection; |
| import java.util.Iterator; |
| |
| import org.apache.felix.eventadmin.impl.handler.EventHandlerProxy; |
| import org.osgi.service.event.Event; |
| |
| |
| /** |
| * This class does the actual work of the synchronous event delivery. |
| * |
| * This is the heart of the event delivery. If an event is delivered |
| * without timeout handling, the event is directly delivered using |
| * the calling thread. |
| * If timeout handling is enabled, a new thread is taken from the |
| * thread pool and this thread is used to deliver the event. |
| * The calling thread is blocked until either the deliver is finished |
| * or the timeout occurs. |
| * <p> |
| * Note that in case of a timeout while a task is disabled the thread |
| * is released and we spin-off a new thread that resumes the disabled |
| * task hence, this is the only place were we break the semantics of |
| * the synchronous delivery. While the only one to notice this is the |
| * timed-out handler - it is the fault of this handler too (i.e., it |
| * blocked the dispatch for to long) but since it will not receive |
| * events anymore it will not notice this semantic difference except |
| * that it might not see events it already sent before. |
| * |
| * If during an event delivery a new event should be delivered from |
| * within the event handler, the timeout handler is stopped for the |
| * delivery time of the inner event! |
| * |
| * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| */ |
| public class SyncDeliverTasks |
| { |
| |
| /** The thread pool used to spin-off new threads. */ |
| private final DefaultThreadPool pool; |
| |
| private long timeout; |
| |
| /** |
| * Construct a new sync deliver tasks. |
| * @param pool The thread pool used to spin-off new threads. |
| * @param timeout The timeout configuration |
| */ |
| public SyncDeliverTasks(final DefaultThreadPool pool, final long timeout) |
| { |
| this.pool = pool; |
| this.update(timeout); |
| } |
| |
| /** |
| * Update the timeout configuration |
| * @param timeout The timeout configuration |
| */ |
| public void update(final long timeout) |
| { |
| this.timeout = timeout; |
| } |
| |
| /** |
| * This blocks an unrelated thread used to send a synchronous event until the |
| * event is send (or a timeout occurs). |
| * |
| * @param tasks The event handler dispatch tasks to execute |
| * @param event The event |
| * @param filterAsyncUnordered async handling |
| * |
| */ |
| public void execute(final Collection<EventHandlerProxy> tasks, final Event event, final boolean filterAsyncUnordered) |
| { |
| final Thread sleepingThread = Thread.currentThread(); |
| final SyncThread syncThread = sleepingThread instanceof SyncThread ? (SyncThread)sleepingThread : null; |
| |
| final Iterator<EventHandlerProxy> i = tasks.iterator(); |
| final DenylistLatch handlerLatch = new DenylistLatch(tasks.size(), this.timeout/2); |
| |
| while ( i.hasNext() ) |
| { |
| final EventHandlerProxy task = i.next(); |
| HandlerTask handlerTask = new HandlerTask(task, event, this.timeout, handlerLatch); |
| // if ( !filterAsyncUnordered || task.isAsyncOrderedDelivery() ) |
| // { |
| if( !handlerTask.useTimeout() ) |
| { |
| handlerTask.runWithoutDenylistTiming(); |
| } |
| else if ( syncThread != null ) |
| { |
| // if this is a cascaded event, we directly use this thread |
| // otherwise we could end up in a starvation |
| handlerTask.run(); |
| } |
| else |
| { |
| |
| handlerLatch.addToDenylistCheck(handlerTask); |
| if ( !this.pool.executeTask(handlerTask) ) |
| { |
| // scheduling failed: last resort, call directly |
| handlerTask.run(); |
| } |
| } |
| |
| // } |
| } |
| handlerLatch.awaitAndDenylistCheck(); |
| |
| } |
| } |