@@ -45,6 +45,11 @@ internal class BackgroundCoroutineWorkQueueExecutor<WorkItem : CoroutineWorkItem
4545 */
4646 private val queueThread = Worker .start()
4747
48+ /* *
49+ * Special worker for IO work
50+ */
51+ private val ioWorker = Worker .start(name = ioWorkerName)
52+
4853 /* *
4954 * The wrapped (allow freezing and mutable access on single thread) queue of WorkItems
5055 */
@@ -84,32 +89,52 @@ internal class BackgroundCoroutineWorkQueueExecutor<WorkItem : CoroutineWorkItem
8489 }.result
8590
8691 /* *
87- * Queues an item to be executed
92+ * Queues an item to be executed in the general worker pool
8893 */
89- fun enqueueWork (item : WorkItem ) = queueThread.executeAfter(
90- operation = {
91- queue.enqueue(item)
92- // start a worker if we have more workers to start
93- val activeWorkerCount = _numActiveWorkers .value
94- if (activeWorkerCount < numWorkers) {
95- pool.performWork {
94+ fun enqueueWork (item : WorkItem , isIoWork : Boolean ) {
95+ if (isIoWork) {
96+ ioWorker.executeAfter(
97+ operation = {
9698 runBlocking {
97- // error if we accidentally freeze coroutine internals
9899 this .ensureNeverFrozen()
99- processWorkItems( )
100+ performWorkHandlingExceptions(item, this )
100101 }
101- }
102- _numActiveWorkers .increment()
103- }
104- }.freeze()
105- )
102+ }.freeze()
103+ )
104+ } else {
105+ queueThread.executeAfter(
106+ operation = {
107+ queue.enqueue(item)
108+ // start a worker if we have more workers to start
109+ val activeWorkerCount = _numActiveWorkers .value
110+ if (activeWorkerCount < numWorkers) {
111+ pool.performWork {
112+ runBlocking {
113+ // error if we accidentally freeze coroutine internals
114+ this .ensureNeverFrozen()
115+ processWorkItems(this )
116+ }
117+ }
118+ _numActiveWorkers .increment()
119+ }
120+ }.freeze()
121+ )
122+ }
123+ }
106124
107- suspend fun CoroutineScope. processWorkItems () {
125+ private suspend fun processWorkItems (scope : CoroutineScope ) {
108126 val workItem = dequeueWork() ? : return
109127
128+ performWorkHandlingExceptions(workItem, scope)
129+
130+ // execute a coroutine to attempt to process the next work item, if possible
131+ scope.launch { processWorkItems(scope) }
132+ }
133+
134+ private suspend fun performWorkHandlingExceptions (workItem : WorkItem , scope : CoroutineScope ) {
110135 // Execute the work in a job that can be cancelled
111136 try {
112- async {
137+ scope. async {
113138 workItem.work(this )
114139 }.await()
115140 } catch (_: CancellationException ) {
@@ -122,9 +147,6 @@ internal class BackgroundCoroutineWorkQueueExecutor<WorkItem : CoroutineWorkItem
122147 throw e
123148 }
124149 }
125-
126- // execute a coroutine to attempt to process the next work item, if possible
127- launch { processWorkItems() }
128150 }
129151
130152 init { freeze() }
@@ -136,6 +158,16 @@ internal class BackgroundCoroutineWorkQueueExecutor<WorkItem : CoroutineWorkItem
136158 internal fun setUnhandledExceptionHook (handler : (Throwable ) -> Unit ) {
137159 UNHANDLED_EXCEPTION_HOOK .value = handler.freeze()
138160 }
161+
162+ /* *
163+ * The name of the IO worker
164+ */
165+ private const val ioWorkerName = " com.autodesk.coroutineworker.ioworker"
166+
167+ /* *
168+ * Returns whether we're already running on the IO thread
169+ */
170+ internal fun shouldPerformIoWorkInline () = Worker .current.name == ioWorkerName
139171 }
140172}
141173
0 commit comments