如果带注释的成员没有被特定的块包围,则发出IDE警告



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

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?

我认为这是一种hack,但使用上下文接收器可能会工作。但我不认为它们是用来这样使用的。

您可以声明一个虚拟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
    }
    

    上下文接收者通过。

最新更新