Входящие замки внутри монад в Скале - PullRequest
3 голосов
/ 25 апреля 2019

Мой коллега сказал следующее об использовании Java ReentrantReadWriteLock в некотором коде Scala:

Получение блокировки здесь рискованно.Это «реентерабельный», но это внутренне зависит от контекста потока.F может запускать разные этапы одного и того же вычисления в разных потоках.Вы можете легко вызвать тупик.

F здесь относится к некоторой эффектной монаде.

По сути, я пытаюсь получить один и тот же повторяющийся замок дважды, в пределахта же самая монада.

Может кто-нибудь уточнить, почему это может быть проблемой?

Код разбит на два файла.Самый внешний:

val lock: Resource[F, Unit] = for {
  // some other resource
  _ <- store.writeLock
} yield ()

lock.use { _ => 
  for {
    // stuff
    _ <- EitherT(store.doSomething())
    // other stuff
  } yield () 
}

Затем в store:

import java.util.concurrent.locks.{Lock, ReentrantReadWriteLock}
import cats.effect.{Resource, Sync}

private def lockAsResource[F[_]](lock: Lock)(implicit F: Sync[F]): Resource[F, Unit] =
  Resource.make {
    F.delay(lock.lock())
  } { _ =>
    F.delay(lock.unlock())
  }

private val lock = new ReentrantReadWriteLock
val writeLock: Resource[F, Unit] = lockAsResource(lock.writeLock())

def doSomething(): F[Either[Throwable, Unit]] = writeLock.use { _ =>
  // etc etc
}

writeLock в двух частях кода одинаков, и это cats.effect.Resource[F, Unit]упаковка ReentrantReadWriteLock х writeLock.Есть несколько причин, почему я писал код таким образом, поэтому я не хотел бы углубляться в это.Я просто хотел бы понять, почему (по крайней мере, по мнению моего коллеги) это может привести к поломке.

Кроме того, я хотел бы знать, есть ли в Scala какая-то альтернатива, которая позволила бы что-то подобноебез риска тупиков.

1 Ответ

2 голосов
/ 26 апреля 2019

IIUC ваш вопрос:

Вы ожидаете, что для каждого взаимодействия с ресурсом действия lock.lock и lock.unlock происходят в одном потоке.

1) Нет никакой гарантии, так как вы используете произвольный эффект F здесь. Можно написать реализацию F, которая выполняет каждое действие в новом потоке.

2) Даже если мы предположим, что F - это IO, тогда тело doSomething кто-то может сделать IO.shift. Поэтому следующие действия, включая unlock, будут происходить в другом потоке. Возможно, это невозможно с текущей подписью doSomething, но вы поняли идею.

Кроме того, я хотел бы знать, есть ли какая-либо альтернатива в Scala, которая позволила бы что-то подобное без риска тупиков.

Вы можете взглянуть на scalaz zio STM.

...