ImageBackend.java revision abac2a23443f13b58951d88bf4b972f31a562e3d
1ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin/*
2ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * Copyright (C) 2014 The Android Open Source Project
3ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *
4ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * Licensed under the Apache License, Version 2.0 (the "License");
5ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * you may not use this file except in compliance with the License.
6ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * You may obtain a copy of the License at
7ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *
8ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *      http://www.apache.org/licenses/LICENSE-2.0
9ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *
10ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * Unless required by applicable law or agreed to in writing, software
11ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * distributed under the License is distributed on an "AS IS" BASIS,
12ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * See the License for the specific language governing permissions and
14ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * limitations under the License.
15ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin */
16ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
173830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberlingpackage com.android.camera.processing.imagebackend;
18ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
19ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport com.android.camera.debug.Log;
20abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Linimport com.android.camera.processing.ProcessingService;
21abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Linimport com.android.camera.processing.ProcessingServiceManager;
22abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Linimport com.android.camera.processing.ProcessingTaskConsumer;
2330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport com.android.camera.session.CaptureSession;
2430ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Linimport com.android.camera.util.Size;
2530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin
26ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.HashMap;
27ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.HashSet;
28ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.Map;
29ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.Set;
30ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.Executor;
31ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.ExecutorService;
32ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.Executors;
33ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.ThreadFactory;
34ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.locks.Condition;
35ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.locks.ReentrantLock;
36ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
37ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin/**
3830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * This ImageBackend is created for the purpose of creating a task-running
3930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * infrastructure that has two-level of priority and doing the book-keeping to
4030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * keep track of tasks that use Android Images. Android.media.images are
4130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * critical system resources that MUST be properly managed in order to maintain
4230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * camera application performance. Android.media.images are merely Java handles
4330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * to regions of physically contiguous memory used by the camera hardware as a
4430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * destination for imaging data. In general, this physically contiguous memory
4530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * is not counted as an application resource, but as a system resources held by
4630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * the application and does NOT count against the limits of application memory.
4730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * The performance pressures of both computing and memory resources must often
4830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * be prioritized in releasing Android.media.images in a timely manner. In order
4930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * to properly balance these concerns, most image processing requested should be
5030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * routed through this object. This object is also responsible for releasing
5130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * Android.media image as soon as possible, so as not to stall the camera
5230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * hardware subsystem. Image that reserve these images are a subclass of the
5330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * basic Java Runnable with a few conditions placed upon their run()
5430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * implementation:
55ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * <ol>
5630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * <li>The task will try to release the image as early as possible by calling
5730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * the releaseSemaphoreReference as soon as a reference to the original image is
5830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * no longer required.</li>
5930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * <li>A set of tasks that require ImageData must only happen on the first
6030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * receiveImage call. receiveImage must only be called once per image.</li>
6130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * <li>However, the submitted tasks may spawn new tasks via the appendTask with
6230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * any image that have had a task submitted, but NOT released via
6330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * releaseSemaphoreReference.</li>
6430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * <li>Computation that is dependent on multiple images should be written into
6530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * this task framework in a distributed manner where image task can be computed
6630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * independently and join their results to a common shared object.This style of
6730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * implementation allows for the earliest release of Android Images while
6830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * honoring the resources priorities set by this class. See the Lucky shot
6930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * implementation for a concrete example for this shared object and its
7030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * respective task {@link TaskLuckyShotSession} {@link LuckyShotSession}</li>
71ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * </ol>
72abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * To integrate with the ProcessingServiceManager, ImageBackend also signals to
73abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * the ProcessingServiceManager its processing state by enqueuing
74abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * ImageShadowTasks on each ImageBackend::receiveIamge call. These ImageShadow
75abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * tasks have no implementation, but emulate the processing delay by blocking
76abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * until all tasks submitted and spawned by a particular receiveImage call have
77abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * completed their processing. This emulated functionality ensures that other
78abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * ProcessingTasks associated with Lens Blur and Panorama are not processing
79abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * while the ImageBackend is running. Unfairly, the ImageBackend proceeds with
80abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * its own processing regardless of the state of ImageShadowTask.
81abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * ImageShadowTasks that are associated with ImageBackend tasks that have
82abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * already been completed should return immediately on its process call.
83ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin */
843c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Linpublic class ImageBackend implements ImageConsumer, ImageTaskManager {
85ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
86ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected static final int FAST_THREAD_PRIORITY = Thread.MAX_PRIORITY;
87ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
88ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected static final int SLOW_THREAD_PRIORITY = Thread.NORM_PRIORITY;
89ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
90ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected static final int NUM_THREADS_FAST = 2;
91ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
92ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected static final int NUM_THREADS_SLOW = 2;
93ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
94abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    protected final ProcessingTaskConsumer mProcessingTaskConsumer;
95abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
96abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
97abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Map for TaskImageContainer and the release of ImageProxy Book-keeping
98abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
993830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    protected final Map<ImageToProcess, ImageReleaseProtocol> mImageSemaphoreMap;
100abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
101abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Map for ImageShadowTask and release of blocking on
102abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * ImageShadowTask::process
103abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
104abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    protected final Map<CaptureSession, ImageShadowTask> mShadowTaskMap;
105ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
106ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected final ExecutorService mThreadPoolFast;
107ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
108ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected final ExecutorService mThreadPoolSlow;
109ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
110ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private final static Log.Tag TAG = new Log.Tag("ImageBackend");
111ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
11230ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin    /**
11330ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     * Approximate viewable size (in pixels) for the fast thumbnail in the
11430ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     * current UX definition of the product. Note that these values will be the
115abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * minimum size of FAST_THUMBNAIL target for the CONVERT_TO_RGB_PREVIEW
116abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * task.
11730ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     */
11830ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin    private final static Size FAST_THUMBNAIL_TARGET_SIZE = new Size(160, 100);
11930ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin
12030ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin    /**
12130ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     * A standard viewable size (in pixels) for the filmstrip thumbnail in the
12230ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     * current UX definition of the product. Note that this size is the minimum
12330ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     * size for the Preview on the filmstrip associated with
1244dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * COMPRESS_TO_JPEG_AND_WRITE_TO_DISK task.
12530ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     */
12630ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin    private final static Size FILMSTRIP_THUMBNAIL_TARGET_SIZE = new Size(512, 384);
12730ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin
128ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    // Some invariants to know that we're keeping track of everything
129ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    // that reflect the state of mImageSemaphoreMap
130ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private int mOutstandingImageRefs = 0;
131ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
132ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private int mOutstandingImageOpened = 0;
133ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
134ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private int mOutstandingImageClosed = 0;
135ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
136ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    // Objects that may be registered to this objects events.
137ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private ImageProcessorProxyListener mProxyListener = null;
138ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
139ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    // Default constructor, values are conservatively targeted to the Nexus 6
140abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    public ImageBackend(ProcessingTaskConsumer processingTaskConsumer) {
141ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolFast = Executors.newFixedThreadPool(NUM_THREADS_FAST, new FastThreadFactory());
142ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolSlow = Executors.newFixedThreadPool(NUM_THREADS_SLOW, new SlowThreadFactory());
143ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mProxyListener = new ImageProcessorProxyListener();
1443830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        mImageSemaphoreMap = new HashMap<>();
145abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        mShadowTaskMap = new HashMap<>();
146abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        mProcessingTaskConsumer = processingTaskConsumer;
147ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
148ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
149ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
150ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * Direct Injection Constructor for Testing purposes.
151ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
152ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param fastService Service where Tasks of FAST Priority are placed.
153ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param slowService Service where Tasks of SLOW Priority are placed.
154ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param imageProcessorProxyListener iamge proxy listener to be used
155ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
156ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    public ImageBackend(ExecutorService fastService, ExecutorService slowService,
157abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            ImageProcessorProxyListener imageProcessorProxyListener,
158abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            ProcessingTaskConsumer processingTaskConsumer) {
159ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolFast = fastService;
160ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolSlow = slowService;
161ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mProxyListener = imageProcessorProxyListener;
1623830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        mImageSemaphoreMap = new HashMap<>();
163abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        mShadowTaskMap = new HashMap<>();
164abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        mProcessingTaskConsumer = processingTaskConsumer;
165ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
166ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
167ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
1683c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * Simple getter for the associated listener object associated with this
16930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * instantiation that handles registration of events listeners.
170ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
171ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @return listener proxy that handles events messaging for this object.
172ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
173ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    public ImageProcessorProxyListener getProxyListener() {
174ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return mProxyListener;
175ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
176ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
177abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
178abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Sets a listener that receive events from all tasks, regardless of image
179abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
180abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param listener Listener to receive all events
181abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
182ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    public void setCameraImageProcessorListener(ImageProcessorListener listener) {
183ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        this.mProxyListener.registerListener(listener, null);
184ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
185ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
186ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
18730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Wrapper function for all log messages created by this object. Default
18830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * implementation is to send messages to the Android logger. For test
18930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * purposes, this method can be overridden to avoid "Stub!" Runtime
19030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * exceptions in Unit Tests.
191ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
192ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    public void logWrapper(String message) {
1930745f6eb1a8cbed85761e02839367ed8b2ff6ef6I-Jong Lin        Log.v(TAG, message);
194ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
195ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
196ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
197ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @return Number of Image references currently held by this instance
198ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
199ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
200abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    public int getNumberOfReservedOpenImages() {
201ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        synchronized (mImageSemaphoreMap) {
202ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // since mOutstandingImageOpened, mOutstandingImageClosed reflect
203ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // the historical state of mImageSemaphoreMap, we need to lock on
204ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // before we return a value.
205ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return mOutstandingImageOpened - mOutstandingImageClosed;
206ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
207ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
208ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
209ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
210abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Returns of the number of receiveImage calls that are currently enqueued
211abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * and/or being processed.
212abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
213abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @return The number of receiveImage calls that are currently enqueued
214abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *         and/or being processed
215abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
216abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    @Override
217abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    public int getNumberOfOutstandingCalls() {
218abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        synchronized (mShadowTaskMap) {
219abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            return mShadowTaskMap.size();
220abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
221abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
222abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
223abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
22430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Signals the ImageBackend that a tasks has released a reference to the
22530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * image. Imagebackend determines whether all references have been released
22630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * and applies its specified release protocol of closing image and/or
22730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * unblocking the caller. Should ONLY be called by the tasks running on this
22830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * class.
229ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
230ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param img the image to be released by the task.
23130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param executor the executor on which the image close is run. if null,
23230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            image close is run by the calling thread (usually the main
23330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            task thread).
234ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
2353c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin    @Override
2363830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public void releaseSemaphoreReference(final ImageToProcess img, Executor executor) {
237ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        synchronized (mImageSemaphoreMap) {
238ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            ImageReleaseProtocol protocol = mImageSemaphoreMap.get(img);
239ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            if (protocol == null || protocol.getCount() <= 0) {
240ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // That means task implementation has allowed an unbalanced
241ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // semaphore release.
242ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                throw new RuntimeException(
243ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                        "ERROR: Task implementation did NOT balance its release.");
244ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
245ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
246ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // Normal operation from here.
247ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            protocol.addCount(-1);
248ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mOutstandingImageRefs--;
249ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            logWrapper("Ref release.  Total refs = " + mOutstandingImageRefs);
250ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            if (protocol.getCount() == 0) {
251ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Image is ready to be released
252ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Remove the image from the map so that it may be submitted
253ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // again.
254ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                mImageSemaphoreMap.remove(img);
255ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
256ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Conditionally close the image, specified by initial
257ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // receiveImage call
258ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                if (protocol.closeOnRelease) {
259ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    closeImageExecutorSafe(img, executor);
26030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin                    logWrapper("Ref release close.");
261ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                }
262ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
263ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Conditionally signal the blocking thread to go.
264ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                if (protocol.blockUntilRelease) {
265ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    protocol.signal();
266ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                }
267ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            } else {
268ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Image is still being held by other tasks.
269ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Otherwise, update the semaphore
270ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                mImageSemaphoreMap.put(img, protocol);
271ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
272ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
273ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
274ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
275ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
2763c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * Spawns dependent tasks from internal implementation of a set of tasks. If
2773c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * a dependent task does NOT require the image reference, it should be
2783c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * passed a null pointer as an image reference. In general, this method
2793c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * should be called after the task has completed its own computations, but
2803c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * before it has released its own image reference (via the
2813c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * releaseSemaphoreReference call).
282ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
283ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param tasks The set of tasks to be run
284ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @return whether tasks are successfully submitted.
285ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
2863c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin    @Override
2873830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public boolean appendTasks(ImageToProcess img, Set<TaskImageContainer> tasks) {
288ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Make sure that referred images are all the same, if it exists.
289ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // And count how image references need to be kept track of.
290ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        int countImageRefs = numPropagatedImageReferences(img, tasks);
291ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
292ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (img != null) {
293ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // If you're still holding onto the reference, make sure you keep
294ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // count
295ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            incrementSemaphoreReferenceCount(img, countImageRefs);
296ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
297ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
298abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // Update the done count on the new tasks.
299abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        incrementTaskDone(tasks);
300abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
301ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        scheduleTasks(tasks);
302ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return true;
303ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
304ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
305ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
306ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * Spawns a single dependent task from internal implementation of a task.
307ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
308ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param task The task to be run
309ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @return whether tasks are successfully submitted.
310ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
3113c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin    @Override
3123830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public boolean appendTasks(ImageToProcess img, TaskImageContainer task) {
313ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        Set<TaskImageContainer> tasks = new HashSet<TaskImageContainer>(1);
314ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        tasks.add(task);
315ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return appendTasks(img, tasks);
316ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
317ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
318ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
31930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Implements that top-level image single task submission that is defined by
32030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * the ImageConsumer interface.
321ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
322ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param img Image required by the task
323ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param task Task to be run
32430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param blockUntilImageRelease If true, call blocks until the object img
32530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is no longer referred by any task. If false, call is
32630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            non-blocking
32730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param closeOnImageRelease If true, images is closed when the object img
32830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is is no longer referred by any task. If false, After an image
32930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is submitted, it should never be submitted again to the
33030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            interface until all tasks and their spawned tasks are
331ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *            finished.
332ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
333ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
3343830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public boolean receiveImage(ImageToProcess img, TaskImageContainer task,
3353c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin            boolean blockUntilImageRelease, boolean closeOnImageRelease)
336ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            throws InterruptedException {
337ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        Set<TaskImageContainer> passTasks = new HashSet<TaskImageContainer>(1);
338ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        passTasks.add(task);
3393c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin        return receiveImage(img, passTasks, blockUntilImageRelease, closeOnImageRelease);
340ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
341ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
342ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
34330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Implements that top-level image single task submission that is defined by
34430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * the ImageConsumer interface.
345ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
346ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param img Image required by the task
347ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param tasks A set of Tasks to be run
34830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param blockUntilImageRelease If true, call blocks until the object img
34930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is no longer referred by any task. If false, call is
35030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            non-blocking
35130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param closeOnImageRelease If true, images is closed when the object img
35230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is is no longer referred by any task. If false, After an image
35330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is submitted, it should never be submitted again to the
35430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            interface until all tasks and their spawned tasks are
355ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *            finished.
356ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @return whether the blocking completed properly
357ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
358ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
3593830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public boolean receiveImage(ImageToProcess img, Set<TaskImageContainer> tasks,
3603c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin            boolean blockUntilImageRelease, boolean closeOnImageRelease)
361ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            throws InterruptedException {
362ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
363ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Short circuit if no tasks submitted.
364ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (tasks == null || tasks.size() <= 0) {
365ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return false;
366ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
367ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
368ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (img == null) {
369ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // TODO: Determine whether you need to be so strict at the top level
370ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            throw new RuntimeException("ERROR: Initial call must reference valid Image!");
371ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
372ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
373ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Make sure that referred images are all the same, if it exists.
374ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // And count how image references need to be kept track of.
375ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        int countImageRefs = numPropagatedImageReferences(img, tasks);
376ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
377abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // Initialize the counters for process-level tasks
378abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        initializeTaskDone(tasks);
379abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
380ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Set the semaphore, given that the number of tasks that need to be
381ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // scheduled
382ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // and the boolean flags for imaging closing and thread blocking
383ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        ImageReleaseProtocol protocol = setSemaphoreReferenceCount(img, countImageRefs,
384ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                blockUntilImageRelease, closeOnImageRelease);
385ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
386ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Put the tasks on their respective queues.
387ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        scheduleTasks(tasks);
388ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
389ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Implement blocking if required
390ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (protocol.blockUntilRelease) {
391ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            protocol.block();
392ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
393ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
394ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return true;
395ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
396ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
397ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
39830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Implements that top-level image task submission short-cut that is defined
39930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * by the ImageConsumer interface.
400ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
401ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param img Image required by the task
40230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param executor Executor to run events and image closes, in case of
40330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            control leakage
40430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param processingFlags Magical bit vector that specifies jobs to be run
40530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            After an image is submitted, it should never be submitted
40630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            again to the interface until all tasks and their spawned tasks
40730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            are finished.
408ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
409ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
4103830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public boolean receiveImage(ImageToProcess img, Executor executor,
41130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin            Set<ImageTaskFlags> processingFlags, CaptureSession session)
41230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin            throws InterruptedException {
413ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
414ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        Set<TaskImageContainer> tasksToExecute = new HashSet<TaskImageContainer>();
415ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
416ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (img == null) {
417ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // No data to process, just pure message.
418ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return true;
419ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
420ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
421ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Now add the pre-mixed versions of the tasks.
422ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
4234dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        if (processingFlags.contains(ImageTaskFlags.COMPRESS_TO_JPEG_AND_WRITE_TO_DISK)) {
4244dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            if (processingFlags.contains(ImageTaskFlags.CREATE_EARLY_FILMSTRIP_PREVIEW)) {
4254dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                // Request job that creates both filmstrip thumbnail from YUV,
426abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // JPEG compression of the YUV Image, and writes the result to
427abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // disk
4284dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                tasksToExecute.add(new TaskPreviewChainedJpeg(img, executor, this, session,
4294dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                        FILMSTRIP_THUMBNAIL_TARGET_SIZE));
4304dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            } else {
431abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // Request job that only does JPEG compression and writes the
432abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // result to disk
4334dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                tasksToExecute.add(new TaskCompressImageToJpeg(img, executor, this, session));
4344dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            }
435ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
436ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
4374dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        if (processingFlags.contains(ImageTaskFlags.CONVERT_TO_RGB_PREVIEW)) {
4384dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // Add an additional type of task to the appropriate queue.
43930ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin            tasksToExecute.add(new TaskConvertImageToRGBPreview(img, executor,
44030ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                    this, TaskImageContainer.ProcessingPriority.FAST, session,
44130ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                    FAST_THUMBNAIL_TARGET_SIZE,
44230ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                    TaskConvertImageToRGBPreview.ThumbnailShape.SQUARE_ASPECT_CIRCULAR_INSET));
443ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
444ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
445ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        receiveImage(img, tasksToExecute,
4464dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                processingFlags.contains(ImageTaskFlags.BLOCK_UNTIL_ALL_TASKS_RELEASE),
4474dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                processingFlags.contains(ImageTaskFlags.CLOSE_ON_ALL_TASKS_RELEASE));
448ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
449ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return true;
450ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
451ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
452ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
453ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * Factory functions, in case, you want some shake and bake functionality.
454ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
4553830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public TaskConvertImageToRGBPreview createTaskConvertImageToRGBPreview(
4563830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling            ImageToProcess image, Executor executor, ImageBackend imageBackend,
45730ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin            CaptureSession session, Size targetSize,
45830ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin            TaskConvertImageToRGBPreview.ThumbnailShape thumbnailShape) {
45930ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin        return new TaskConvertImageToRGBPreview(image, executor, imageBackend,
46030ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                TaskImageContainer.ProcessingPriority.FAST, session,
46130ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                FAST_THUMBNAIL_TARGET_SIZE, thumbnailShape);
462ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
463ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
4643830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public TaskCompressImageToJpeg createTaskCompressImageToJpeg(ImageToProcess image,
46530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin            Executor executor, ImageBackend imageBackend, CaptureSession session) {
4663830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        return new TaskCompressImageToJpeg(image, executor, imageBackend, session);
467ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
468ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
469ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
470ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * Blocks and waits for all tasks to complete.
471ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
472ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
473ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    public void shutdown() {
474ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolSlow.shutdown();
475ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolFast.shutdown();
476ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
477ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
478ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
479abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * For a given set of starting tasks, initialize the associated sessions
480abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * with a proper blocking semaphore and value of number of tasks to be run.
481abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * For each semaphore, a ImageShadowTask will be instantiated and enqueued
482abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * onto the selected ProcessingSerivceManager.
483abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
484abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param tasks The set of ImageContainer tasks to be run on ImageBackend
485abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
486abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    protected void initializeTaskDone(Set<TaskImageContainer> tasks) {
487abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        Set<CaptureSession> sessionSet = new HashSet<>();
488abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        Map<CaptureSession, Integer> sessionTaskCount = new HashMap<>();
489abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
490abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // Create a set w/ no session duplicates and count them
491abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        for (TaskImageContainer task : tasks) {
492abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            sessionSet.add(task.mSession);
493abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            Integer currentCount = sessionTaskCount.get(task.mSession);
494abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            if (currentCount == null) {
495abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                sessionTaskCount.put(task.mSession, 1);
496abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            } else {
497abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                sessionTaskCount.put(task.mSession, currentCount + 1);
498abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            }
499abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
500abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
501abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // Create a new blocking semaphore for each set of tasks on a given
502abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // session.
503abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        synchronized (mShadowTaskMap) {
504abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            for (CaptureSession captureSession : sessionSet) {
505abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                BlockSignalProtocol protocol = new BlockSignalProtocol();
506abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                protocol.setCount(sessionTaskCount.get(captureSession));
507abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                ImageShadowTask shadowTask = new ImageShadowTask(protocol, captureSession);
508abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                mShadowTaskMap.put(captureSession, shadowTask);
509abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                mProcessingTaskConsumer.enqueueTask(shadowTask);
510abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            }
511abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
512abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
513abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
514abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
515abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * For ImageBackend tasks that spawn their own tasks, increase the semaphore
516abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * count to take into account the new tasks being spawned.
517abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
518abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param tasks The set of tasks to be spawned.
519abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
520abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    protected void incrementTaskDone(Set<TaskImageContainer> tasks) throws RuntimeException {
521abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // TODO: Add invariant test so that all sessions are the same.
522abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        synchronized (mShadowTaskMap) {
523abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            for (TaskImageContainer task : tasks) {
524abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                ImageShadowTask shadowTask = mShadowTaskMap.get(task.mSession);
525abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                if (shadowTask == null) {
526abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                    throw new RuntimeException(
527abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                            "Session NOT previously registered."
528abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                                    + " ImageShadowTask booking-keeping is incorrect.");
529abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                }
530abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                shadowTask.getProtocol().addCount(1);
531abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            }
532abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
533abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
534abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
535abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
536abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Decrement the semaphore count of the ImageShadowTask. Should be called
537abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * when a task completes its processing in ImageBackend.
538abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
539abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param imageShadowTask The ImageShadow task that contains the blocking
540abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *            semaphore.
541abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
542abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    protected void decrementTaskDone(ImageShadowTask imageShadowTask) {
543abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        synchronized (mShadowTaskMap) {
544abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            int remainingTasks = imageShadowTask.getProtocol().addCount(-1);
545abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            if (remainingTasks == 0) {
546abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                mShadowTaskMap.remove(imageShadowTask.getSession());
547abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                imageShadowTask.getProtocol().signal();
548abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            }
549abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
550abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
551abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
552abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
553abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
55430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Puts the tasks on the specified queue. May be more complicated in the
55530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * future.
556ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
557ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param tasks The set of tasks to be run
558ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
559ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected void scheduleTasks(Set<TaskImageContainer> tasks) {
560abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        synchronized (mShadowTaskMap) {
561abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            for (TaskImageContainer task : tasks) {
562abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                ImageShadowTask shadowTask = mShadowTaskMap.get(task.mSession);
563abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                if (shadowTask == null) {
564abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                    throw new IllegalStateException("Scheduling a task with a unknown session.");
565abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                }
566abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // Before scheduling, wrap TaskImageContainer inside of the
567abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // TaskDoneWrapper to add
568abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // instrumentation for managing ImageShadowTasks
569abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                if (task.getProcessingPriority() == TaskImageContainer.ProcessingPriority.FAST) {
570abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                    mThreadPoolFast.execute(new TaskDoneWrapper(this, shadowTask, task));
571abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                } else {
572abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                    mThreadPoolSlow.execute(new TaskDoneWrapper(this, shadowTask, task));
573abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                }
574ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
575ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
576ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
577ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
578ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
579ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * Initializes the semaphore count for the image
580ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
58130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @return The protocol object that keeps tracks of the image reference
58230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *         count and actions to be taken on release.
583ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
5843830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    protected ImageReleaseProtocol setSemaphoreReferenceCount(ImageToProcess img, int count,
585ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            boolean blockUntilRelease, boolean closeOnRelease) throws RuntimeException {
586ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        synchronized (mImageSemaphoreMap) {
587ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            if (mImageSemaphoreMap.get(img) != null) {
588ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                throw new RuntimeException(
589abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                        "ERROR: Rewriting of Semaphore Lock."
590abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                                + "  Image references may not freed properly");
591ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
592ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
593ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // Create the new booking-keeping object.
594ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            ImageReleaseProtocol protocol = new ImageReleaseProtocol(blockUntilRelease,
595ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    closeOnRelease);
596abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            protocol.setCount(count);
597ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
598ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mImageSemaphoreMap.put(img, protocol);
599ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mOutstandingImageRefs += count;
600ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mOutstandingImageOpened++;
601ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            logWrapper("Received an opened image: " + mOutstandingImageOpened + "/"
602ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    + mOutstandingImageClosed);
603ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            logWrapper("Setting an image reference count of " + count + "   Total refs = "
604ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    + mOutstandingImageRefs);
605ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return protocol;
606ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
607ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
608ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
609ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
61030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Increments the semaphore count for the image. Should ONLY be internally
61130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * via appendTasks by internal tasks. Otherwise, image references could get
61230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * out of whack.
613abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
614abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param img The Image associated with the set of tasks running on it.
615abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param count The number of tasks to be added
616abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @throws RuntimeException Indicates image Closing Bookkeeping is screwed
617abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *             up.
618ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
6193830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    protected void incrementSemaphoreReferenceCount(ImageToProcess img, int count)
620ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            throws RuntimeException {
621ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        synchronized (mImageSemaphoreMap) {
622ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            ImageReleaseProtocol protocol = mImageSemaphoreMap.get(img);
623ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            if (mImageSemaphoreMap.get(img) == null) {
624ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                throw new RuntimeException(
625ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                        "Image Reference has already been released or has never been held.");
626ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
627ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
628ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            protocol.addCount(count);
629ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mImageSemaphoreMap.put(img, protocol);
630ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
631ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mOutstandingImageRefs += count;
632ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
633ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
634ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
635ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
63630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Close an Image with a executor if it's available and does the proper
63730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * booking keeping on the object.
638ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
639ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param img Image to be closed
64030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param executor Executor to be used, if executor is null, the close is
64130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            run on the task thread
642ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
6433830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    private void closeImageExecutorSafe(final ImageToProcess img, Executor executor) {
644ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        Runnable closeTask = new Runnable() {
645ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            @Override
646ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            public void run() {
6473830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling                img.proxy.close();
648ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                mOutstandingImageClosed++;
649ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                logWrapper("Release of image occurred.  Good fun. " + "Total Images Open/Closed = "
650ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                        + mOutstandingImageOpened + "/" + mOutstandingImageClosed);
651ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
652ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        };
653ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (executor == null) {
654ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // Just run it on the main thread.
655ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            closeTask.run();
656ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        } else {
657ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            executor.execute(closeTask);
658ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
659ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
660ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
661ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
66230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Calculates the number of new Image references in a set of dependent
66330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * tasks. Checks to make sure no new image references are being introduced.
664ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
665ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param tasks The set of dependent tasks to be run
666ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
6673830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    private int numPropagatedImageReferences(ImageToProcess img, Set<TaskImageContainer> tasks)
668ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            throws RuntimeException {
669ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        int countImageRefs = 0;
670ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        for (TaskImageContainer task : tasks) {
6713830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling            if (task.mImage != null && task.mImage != img) {
672ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                throw new RuntimeException("ERROR:  Spawned tasks cannot reference new images!");
673ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
674ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
6753830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling            if (task.mImage != null) {
676ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                countImageRefs++;
677ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
678ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
679ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
680ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return countImageRefs;
681ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
682ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
683ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
684abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Simple wrapper task to instrument when tasks ends so that ImageBackend
685abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * can fire events when set of tasks created by a ReceiveImage call have all
686abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * completed.
687ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
688abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    private class TaskDoneWrapper implements Runnable {
689abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        private final ImageBackend mImageBackend;
690abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        private final ImageShadowTask mImageShadowTask;
691abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        private final Runnable mTaskToRun;
692abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
693abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        /**
694abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         * Constructor
695abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         *
696abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         * @param imageBackend ImageBackend that the task is running on
697abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         * @param imageShadowTask ImageShadowTask that is blocking on the
698abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         *            completion of the task
699abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         * @param taskToRun The task to be run w/o instrumentation
700abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         */
701abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        public TaskDoneWrapper(ImageBackend imageBackend, ImageShadowTask imageShadowTask,
702abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                Runnable taskToRun) {
703abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            mImageBackend = imageBackend;
704abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            mImageShadowTask = imageShadowTask;
705abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            mTaskToRun = taskToRun;
706abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
707ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
708abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        /**
709abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         * Adds instrumentation that runs when a Runnable completes.
710abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         */
711abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        @Override
712abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        public void run() {
713abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            mTaskToRun.run();
714abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            // Decrement count
715abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            mImageBackend.decrementTaskDone(mImageShadowTask);
716abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
717abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
718ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
719abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
720abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Encapsulates all synchronization for semaphore signaling and blocking.
721abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
722abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    static public class BlockSignalProtocol {
723ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        private int count;
724ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
725ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        private final ReentrantLock mLock = new ReentrantLock();
726ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
727ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        private Condition mSignal;
728ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
729ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public void setCount(int value) {
730ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.lock();
731ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            count = value;
732ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.unlock();
733ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
734ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
735ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public int getCount() {
736ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            int value;
737ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.lock();
738ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            value = count;
739ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.unlock();
740ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return value;
741ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
742ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
743abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        public int addCount(int value) {
744ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.lock();
745abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            try {
746abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                count += value;
747abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                return count;
748abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            } finally {
749abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                mLock.unlock();
750abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            }
751ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
752ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
753abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        BlockSignalProtocol() {
754ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            count = 0;
755ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mSignal = mLock.newCondition();
756ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
757ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
758ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public void block() throws InterruptedException {
759ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.lock();
760ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            try {
761ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                while (count != 0) {
762ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    // Spin to deal with spurious signals.
763ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    mSignal.await();
764ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                }
765ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            } catch (InterruptedException e) {
766ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // TODO: on interruption, figure out what to do.
767ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                throw (e);
7683c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin            } finally {
7693c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin                mLock.unlock();
770ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
771ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
772ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
773ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public void signal() {
774ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.lock();
775ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mSignal.signal();
776ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.unlock();
777ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
778ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
779ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
780ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
781abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
782abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * A simple tuple class to keep track of image reference, and whether to
783abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * block and/or close on final image release. Instantiated on every task
784abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * submission call.
785abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
786abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    static public class ImageReleaseProtocol extends BlockSignalProtocol {
787abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
788abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        public final boolean blockUntilRelease;
789abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
790abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        public final boolean closeOnRelease;
791abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
792abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        ImageReleaseProtocol(boolean block, boolean close) {
793abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            super();
794abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            blockUntilRelease = block;
795abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            closeOnRelease = close;
796abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
797abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
798abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
799abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
800ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    // Thread factories for a default constructor
801ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private class FastThreadFactory implements ThreadFactory {
802ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
803ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public Thread newThread(Runnable r) {
804ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            Thread t = new Thread(r);
805ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            t.setPriority(FAST_THREAD_PRIORITY);
806ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return t;
807ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
808ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
809ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
810ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private class SlowThreadFactory implements ThreadFactory {
811ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
812ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public Thread newThread(Runnable r) {
813ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            Thread t = new Thread(r);
814ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            t.setPriority(SLOW_THREAD_PRIORITY);
815ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return t;
816ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
817ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
818ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
819ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin}
820