kotlin 如果注解的成员未被特定块包围,则发出IDE警告

mzaanser  于 2022-11-16  发布在  Kotlin
关注(0)|答案(1)|浏览(127)

我有一个数据结构,它的成员不是线程安全的,调用者需要锁定资源,以便适当地阅读。下面是一个最小的代码示例:

class ExampleResource : LockableProjectItem {
    override val readWriteLock: ReadWriteLock = ReentrantReadWriteLock()

    @RequiresReadLock
    val nonThreadSafeMember: String = ""
}

interface LockableProjectItem {
    val readWriteLock: ReadWriteLock
}

fun <T : LockableProjectItem, Out> T.readLock(block: T.() -> Out): Out {
    try {
        readWriteLock.readLock().lock()
        return block(this)
    } finally {
        readWriteLock.readLock().unlock()
    }
}

fun <T : LockableProjectItem, Out> T.writeLock(block: T.() -> Out): Out {
    try {
        readWriteLock.writeLock().lock()
        return block(this)
    } finally {
        readWriteLock.writeLock().unlock()
    }
}

annotation class RequiresReadLock

调用ExampleResource.nonThreadSafeMember可能如下所示:

val resource = ExampleResource()
val readResult = resource.readLock { nonThreadSafeMember }

为了确保调用者知道需要锁定资源,我希望IDE对使用@RequiresReadLock注解并且没有使用readLock块包围的任何成员发出警告。有没有办法在IntelliJ中执行此操作,而不需要为IDE编写自定义插件?

7tofc5zh

7tofc5zh1#

我认为这是一种黑客,但使用context receivers可能会工作。我不认为他们打算以这种方式使用。
您可以声明一个伪object作为上下文接收器,并将其作为上下文接收器添加到属性中:

object ReadLock

class ExampleResource : LockableProjectItem {
    override val readWriteLock: ReadWriteLock = ReentrantReadWriteLock()

    // properties with context receivers cannot have a backing field, so we need to explicitly declare this
    private val nonThreadSafeMemberField: String = ""

    context(ReadLock)
    val nonThreadSafeMember: String
        get() = nonThreadSafeMemberField
}

然后在readLock中,传递object

fun <T : LockableProjectItem, Out> T.readLock(block: context(ReadLock) T.() -> Out): Out {
    try {
        readWriteLock.readLock().lock()
        return block(ReadLock, this)
    } finally {
        readWriteLock.readLock().unlock()
    }
}

备注:

  • 如果您尝试在没有上下文接收器的情况下访问nonThreadSafeMember,则会给予错误:
val resource = ExampleResource()
val readResult = resource.nonThreadSafeMember //error
  • 您仍然可以访问nonThreadSafeMember,而无需获取读锁定,方法是执行以下操作:
with(ReadLock) { // with(ReadLock) doesn't acquire the lock, just gets the context receiver
    resource.nonThreadSafeMember // no error
}

但是很难 * 意外 * 写这样的东西,我认为这是你试图防止的。

  • 如果你在readLock中调用另一个函数,并且你想在那个函数中访问nonThreadSafeMember,你也应该用context(ReadLock)来标记那个函数。
fun main() {
    val resource = ExampleResource()
    val readResult = resource.readLock {
        foo(this)
    }
}

context(ReadLock)
fun foo(x: ExampleResource) {
    x.nonThreadSafeMember
}

内容接收端会透过传播。

相关问题