Request long-running action if not running

private var fetching: Boolean = false
private fun fetch() {
  if(fetching) return
  fetching = true
  launch {
    _fetch something that takes long_
    fetching = false
  }
}

I often write methods as the one above, that are there to request a long-running action if it is not already runnning. I wondered if there was a construct that could simplify this or something could be implemented to reduce this boilerplate?

Hi Xerus,
this code has a synchronization issue.

However, did you look at Actor or Mutex?

class Something

private val fetchingActor = actor<Something>(capacity = Channel.UNLIMITED) {
 // _fetch something that takes long_
}

private fun fetch(something: Something) {
 fetchingActor.sendBlocking(something)
}

class Something

private val fetchingMutex = Mutex()

private fun fetch(something: Something) {
    launch {
        fetchingMutex.withLock {
         // _fetch something that takes long_
        }
    }
}

Synchronization isn’t an issue for me, since this method will always be called from the same thread.
I think you sort of missed my point, because the idea was not to process incoming things, but to simply request a refresh. If that refresh is already taking place, it should simply return instead of creating a new refresh.

Also: I can’t really find documentation on Actor and Mutex, except for the KDoc in the code.

This is my solution now:

class Refresher(private val runnable: () -> Unit) {
  private var refreshing = false
  fun refresh() {
    if (refreshing) return
    refreshing = true
    launch {
      runnable()
      refreshing = false
    }
  }
}

Hi @Xerus,
you can find the documentaion here:

https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/README.md

Apart of synchronization issue I want sugest you to put runnable() invocation in a try-finally block or use a Job instead of boolean.

class Refresher(private val runnable: () -> Unit) {
    private var currentJob: Job? = null
    operator fun invoke() {
        if (currentJob?.isCompleted != false) {
            currentJob = launch {
                runnable()
            }
        }
    }
}