com.github.dm.jrt.routine.Routine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jroutine Show documentation
Show all versions of jroutine Show documentation
Parallel programming on the go
/*
* 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.
*/
package com.github.dm.jrt.routine;
import com.github.dm.jrt.channel.InvocationChannel;
import com.github.dm.jrt.channel.OutputChannel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* This interface defines the main component of the library architecture.
*
* The interface takes inspiration from the Go routines, where functions are executed
* asynchronously and communicate with the external world only through channels. Being Java a
* strictly object oriented programming language, the routine itself must be an object based on
* logic implemented in other objects.
*
* The library includes a routine class based on the implementation of an invocation interface.
* Invocation objects are dynamically instantiated when needed, effectively mimicking the temporary
* scope of a function call.
* The paradigm is based on input, result and output channels. A routine can be invoked in
* different ways (as explained below). Each routine invocation returns an input channel through
* which the caller can pass the input parameters. When all the parameters has been passed, the
* input channel is closed and returns the output channel from which to read the invocation
* results. At the same time a result channel is passed to the invocation implementation, so that
* the output computed from the input parameters can be published outside.
* The advantage of this approach is that the invocation flow can be run in steps, allowing for
* continuous streaming of the input data and for abortion in the middle of the execution, without
* blocking the running thread for the whole duration of the asynchronous invocation.
* In fact, each channel can abort the execution at any time.
*
* The implementing class must provides an automatic synchronization of the invocation member
* fields, though, in order to avoid concurrency issues, data passed through the routine channels
* should be immutable or, at least, never shared inside and outside the routine.
* Moreover, it is possible to recursively call the same or another routine from inside a routine
* invocation in a safe way. Nevertheless, it is not allow to perform blocking calls (such as
* reading data from an output channel) in the middle of an execution when shared runner instances
* are employed. Additionally, to prevent deadlock or starvation issues, it is encouraged the use of
* finite timeouts when performing blocking calls.
*
* The routine object provides three different ways to invoke an execution:
*
* Synchronous invocation
* The routine starts an invocation employing a synchronous runner. The result is similar to a
* classic method call where the processing completes as soon as the function exits.
*
* Asynchronous invocation
* The routine starts an invocation employing an asynchronous runner. In this case the processing
* happens in a different thread, while the calling process may go on with its own execution and
* perform other tasks. The invocation results can be collected at any time through the output
* channel methods.
*
* Parallel invocation
* Processing parallelization is the key to leverage the power of multi-core machines. In order to
* achieve it, the input data must be divided into subsets which are then processed on different
* threads.
* A routine object provides a convenient way to start an invocation which in turn spawns another
* invocation for each input passed. This particular type of invocation obviously produces
* meaningful results only for routines which takes a single input parameter and computes the
* relative output results.
*
* It is worth noting how the library has been designed only through interfaces, so that, as far as
* the implementation honors the specific contracts, it is possible to seamlessly combine different
* routine instances, even the ones coming from third party libraries.
*
* Created by davide-maestroni on 09/07/2014.
*
* @param the input data type.
* @param the output data type.
*/
public interface Routine {
/**
* Short for {@code asyncInvoke().result()}.
*
* @return the output channel.
*/
@NotNull
OutputChannel asyncCall();
/**
* Short for {@code asyncInvoke().pass(input).result()}.
*
* @param input the input.
* @return the output channel.
*/
@NotNull
OutputChannel asyncCall(@Nullable IN input);
/**
* Short for {@code asyncInvoke().pass(inputs).result()}.
*
* @param inputs the input data.
* @return the output channel.
*/
@NotNull
OutputChannel asyncCall(@Nullable IN... inputs);
/**
* Short for {@code asyncInvoke().pass(inputs).result()}.
*
* @param inputs the iterable returning the input data.
* @return the output channel.
*/
@NotNull
OutputChannel asyncCall(@Nullable Iterable extends IN> inputs);
/**
* Short for {@code asyncInvoke().pass(inputs).result()}.
*
* @param inputs the output channel returning the input data.
* @return the output channel.
*/
@NotNull
OutputChannel asyncCall(@Nullable OutputChannel extends IN> inputs);
/**
* Invokes the execution of this routine in asynchronous mode.
*
* @return the invocation channel.
*/
@NotNull
InvocationChannel asyncInvoke();
/**
* Short for {@code parallelInvoke().result()}.
*
* (This method actually makes little sense, and should never need to be called, thought it is
* here for completeness)
*
* @return the output channel.
*/
@NotNull
OutputChannel parallelCall();
/**
* Short for {@code parallelInvoke().pass(input).result()}.
*
* (This method actually makes little sense, and should never need to be called, thought it is
* here for completeness)
*
* @param input the input.
* @return the output channel.
*/
@NotNull
OutputChannel parallelCall(@Nullable IN input);
/**
* Short for {@code parallelInvoke().pass(inputs).result()}.
*
* @param inputs the input data.
* @return the output channel.
*/
@NotNull
OutputChannel parallelCall(@Nullable IN... inputs);
/**
* Short for {@code parallelInvoke().pass(inputs).result()}.
*
* @param inputs the iterable returning the input data.
* @return the output channel.
*/
@NotNull
OutputChannel parallelCall(@Nullable Iterable extends IN> inputs);
/**
* Short for {@code parallelInvoke().pass(inputs).result()}.
*
* @param inputs the output channel returning the input data.
* @return the output channel.
*/
@NotNull
OutputChannel parallelCall(@Nullable OutputChannel extends IN> inputs);
/**
* Invokes the execution of this routine in parallel mode.
*
* @return the invocation channel.
*/
@NotNull
InvocationChannel parallelInvoke();
/**
* Makes the routine destroy all the cached invocation instances.
*
* This method is useful to force the release of external resources when done with the routine.
* Note however that the routine will still be usable after the method returns.
*
* Normally it is not needed to call this method.
*/
void purge();
/**
* Short for {@code syncInvoke().result()}.
*
* @return the output channel.
*/
@NotNull
OutputChannel syncCall();
/**
* Short for {@code syncInvoke().pass(input).result()}.
*
* @param input the input.
* @return the output channel.
*/
@NotNull
OutputChannel syncCall(@Nullable IN input);
/**
* Short for {@code syncInvoke().pass(inputs).result()}.
*
* @param inputs the input data.
* @return the output channel.
*/
@NotNull
OutputChannel syncCall(@Nullable IN... inputs);
/**
* Short for {@code syncInvoke().pass(inputs).result()}.
*
* @param inputs the iterable returning the input data.
* @return the output channel.
*/
@NotNull
OutputChannel syncCall(@Nullable Iterable extends IN> inputs);
/**
* Short for {@code syncInvoke().pass(inputs).result()}.
*
* @param inputs the output channel returning the input data.
* @return the output channel.
*/
@NotNull
OutputChannel syncCall(@Nullable OutputChannel extends IN> inputs);
/**
* Invokes the execution of this routine in synchronous mode.
*
* @return the invocation channel.
*/
@NotNull
InvocationChannel syncInvoke();
}