Skip to content

Commit 5750483

Browse files
authored
Merge pull request #4 from patricklx/hack-hack-hack
fix lock re-entry
2 parents b31d09a + e77bbb1 commit 5750483

File tree

1 file changed

+86
-65
lines changed

1 file changed

+86
-65
lines changed

src/main/kotlin/com/wsl/symlinks/vfs/WslVirtualFileSystem.kt

+86-65
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,24 @@ package com.wsl.symlinks.vfs
22

33
import ai.grazie.utils.WeakHashMap
44
import com.intellij.ide.AppLifecycleListener
5-
import com.intellij.idea.IdeaLogger
65
import com.intellij.openapi.components.service
7-
import com.intellij.openapi.diagnostic.Attachment
8-
import com.intellij.openapi.diagnostic.DefaultLogger
96
import com.intellij.openapi.diagnostic.Logger
7+
import com.intellij.openapi.extensions.ExtensionPointListener
8+
import com.intellij.openapi.extensions.PluginDescriptor
9+
import com.intellij.openapi.extensions.impl.ExtensionPointImpl
1010
import com.intellij.openapi.util.io.FileAttributes
1111
import com.intellij.openapi.vfs.VirtualFile
1212
import com.intellij.openapi.vfs.VirtualFileManager
1313
import com.intellij.openapi.vfs.VirtualFileSystem
14-
import com.intellij.openapi.vfs.impl.VirtualFileManagerImpl
1514
import com.intellij.openapi.vfs.impl.local.LocalFileSystemImpl
15+
import com.intellij.openapi.vfs.newvfs.impl.FakeVirtualFile
16+
import com.intellij.openapi.vfs.newvfs.impl.StubVirtualFile
1617
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
18+
import com.intellij.util.KeyedLazyInstance
19+
import com.intellij.util.KeyedLazyInstanceEP
1720
import com.intellij.util.io.URLUtil
18-
import com.jetbrains.rd.util.collections.SynchronizedMap
1921
import java.io.*
20-
import java.util.concurrent.ConcurrentLinkedQueue
22+
import java.util.concurrent.ConcurrentHashMap
2123
import java.util.concurrent.LinkedBlockingQueue
2224
import java.util.concurrent.TimeUnit
2325
import java.util.concurrent.locks.ReentrantLock
@@ -27,36 +29,10 @@ import kotlin.concurrent.withLock
2729

2830
class StartupListener: AppLifecycleListener {
2931
override fun appFrameCreated(commandLineArgs: MutableList<String>) {
30-
MyLogger.setup()
31-
}
32-
}
33-
34-
class MyLogger(category: String): DefaultLogger(category) {
35-
36-
override fun error(message: String?) {
37-
if (message?.contains(">1 file system registered for protocol") == true) {
38-
return
39-
}
40-
super.error(message)
41-
}
42-
43-
override fun error(message: String?, t: Throwable?, vararg details: String?) {
44-
if (message?.contains(">1 file system registered for protocol") == true) {
45-
return
46-
}
47-
super.error(message, t, *details)
48-
}
4932

50-
companion object {
51-
fun setup() {
52-
//IdeaLogger.setFactory { category -> MyLogger(category) }
53-
//Logger.setFactory { category -> MyLogger(category) }
54-
}
55-
// val logger = setup()
5633
}
5734
}
5835

59-
val myResourceLock = ReentrantLock()
6036

6137
class WslSymlinksProvider(distro: String) {
6238
val LOGGER = Logger.getInstance(WslSymlinksProvider::class.java)
@@ -65,19 +41,20 @@ class WslSymlinksProvider(distro: String) {
6541
private var processReader: BufferedReader? = null
6642
private var processWriter: BufferedWriter? = null
6743
private val queue: LinkedBlockingQueue<AsyncValue> = LinkedBlockingQueue()
68-
private val mapped: SynchronizedMap<String, AsyncValue> = SynchronizedMap()
44+
private val mapped: ConcurrentHashMap<String, AsyncValue> = ConcurrentHashMap()
45+
val myResourceLock = ReentrantLock()
6946

70-
class AsyncValue {
47+
class AsyncValue(private val lock: ReentrantLock) {
7148
public val id = this.hashCode().toString()
7249
public var request: String? = null
7350
internal var value: String? = null
74-
internal val condition = myResourceLock.newCondition()
51+
internal val condition = lock.newCondition()
7552
fun getValue(): String? {
7653
var elapsed = 0
77-
while (value == null && elapsed < 5000) {
78-
if (myResourceLock.tryLock(10, TimeUnit.MILLISECONDS)) {
54+
while (value == null && elapsed < 1000) {
55+
if (lock.tryLock(10, TimeUnit.MILLISECONDS)) {
7956
condition.await(10, TimeUnit.MILLISECONDS)
80-
myResourceLock.unlock()
57+
lock.unlock()
8158
}
8259
elapsed += 10
8360
}
@@ -89,24 +66,29 @@ class WslSymlinksProvider(distro: String) {
8966
}
9067

9168
init {
92-
val bash = {}.javaClass.getResource("/files.sh")?.readText()!!
93-
val location = "\\\\wsl.localhost\\$distro\\var\\tmp\\intellij-idea-wsl-symlinks.sh"
94-
File(location).writeText(bash)
95-
val builder = ProcessBuilder("wsl.exe", "-d", distro, "-e", "bash", "//var/tmp/intellij-idea-wsl-symlinks.sh")
69+
LOGGER.info("starting WslSymlinksProvider for distro: $distro")
9670

9771

9872
fun setupProcess() {
73+
val bash = {}.javaClass.getResource("/files.sh")?.readText()!!
74+
val location = "\\\\wsl.localhost\\$distro\\var\\tmp\\intellij-idea-wsl-symlinks.sh"
75+
File(location).writeText(bash)
76+
val builder = ProcessBuilder("wsl.exe", "-d", distro, "-e", "bash", "//var/tmp/intellij-idea-wsl-symlinks.sh")
9977
val process = builder.start()
10078
this.process = process
10179
this.processReader = BufferedReader(InputStreamReader(process.inputStream))
10280
this.processWriter = BufferedWriter(OutputStreamWriter(process.outputStream));
81+
LOGGER.info("starting process")
10382
process.onExit().whenComplete { t, u ->
10483
LOGGER.error("process did exit ${u?.message ?: "no-reason"}")
10584
setupProcess()
10685
}
10786
}
10887

109-
setupProcess()
88+
thread {
89+
setupProcess()
90+
}
91+
11092

11193
thread {
11294
while (true) {
@@ -123,7 +105,7 @@ class WslSymlinksProvider(distro: String) {
123105
}
124106
mapped.remove(id)
125107
} catch (e: Exception) {
126-
LOGGER.error("failed to write", e)
108+
LOGGER.error("failed to read", e)
127109
}
128110
}
129111
}
@@ -154,13 +136,18 @@ class WslSymlinksProvider(distro: String) {
154136
if (file.cachedWSLCanonicalPath == null) {
155137
try {
156138
val wslPath = file.getWSLPath()
157-
val a = AsyncValue()
139+
val a = AsyncValue(myResourceLock)
158140
a.request = "${a.id};read-symlink;${wslPath}\n"
159141
this.queue.add(a)
160-
while (a.getValue() == null) {
142+
var n = 0
143+
while (a.getValue() == null && n < 3) {
161144
if (!queue.contains(a)) {
162145
this.queue.add(a)
163146
}
147+
n += 1
148+
}
149+
if (a.getValue() == null) {
150+
return file.path
164151
}
165152
val link = a.getValue()
166153
file.cachedWSLCanonicalPath = file.path.split("/").subList(0, 4).joinToString("/") + link
@@ -179,13 +166,18 @@ class WslSymlinksProvider(distro: String) {
179166
if (file.isFromWSL() && file.parent != null) {
180167
try {
181168
val path: String = file.path.replace("^//wsl\\$/[^/]+".toRegex(), "").replace("""^//wsl.localhost/[^/]+""".toRegex(), "")
182-
val a = AsyncValue()
169+
val a = AsyncValue(myResourceLock)
183170
a.request = "${a.id};is-symlink;${path}\n"
184171
this.queue.add(a)
185-
while (a.getValue() == null) {
172+
var n = 0
173+
while (a.getValue() == null && n < 3) {
186174
if (!queue.contains(a)) {
187175
this.queue.add(a)
188176
}
177+
n += 1
178+
}
179+
if (a.getValue() == null) {
180+
return false
189181
}
190182
val isSymLink = a.getValue()
191183
file.isSymlink = isSymLink.equals("true")
@@ -200,9 +192,30 @@ class WslSymlinksProvider(distro: String) {
200192
}
201193

202194
class WslVirtualFileSystem: LocalFileSystemImpl() {
195+
val LOGGER = Logger.getInstance(WslVirtualFileSystem::class.java)
203196
private var wslSymlinksProviders: MutableMap<String, WslSymlinksProvider> = HashMap()
204-
private var reentry: VirtualFile? = null
205-
val lock = ReentrantLock()
197+
198+
init {
199+
val classNameToUnregister = LocalFileSystemImpl::class.java.canonicalName
200+
VirtualFileSystem.EP_NAME.point.addExtensionPointListener(object : ExtensionPointListener<KeyedLazyInstance<VirtualFileSystem>> {
201+
override fun extensionRemoved(
202+
extension: KeyedLazyInstance<VirtualFileSystem>,
203+
pluginDescriptor: PluginDescriptor
204+
) {
205+
val ext = (extension as? KeyedLazyInstanceEP)
206+
if (ext != null) {
207+
pluginDescriptor.isEnabled = false
208+
ext.implementationClass = null
209+
}
210+
211+
}
212+
}, false, this)
213+
val point: ExtensionPointImpl<Any> = VirtualFileSystem.EP_NAME.point as ExtensionPointImpl<Any>
214+
point.unregisterExtensions({ className, adapter ->
215+
className != "com.intellij.openapi.vfs.impl.VirtualFileManagerImpl\$VirtualFileSystemBean"
216+
|| adapter.createInstance<KeyedLazyInstanceEP<VirtualFileSystem>>(point.componentManager)?.implementationClass != "com.intellij.openapi.vfs.impl.local.LocalFileSystemImpl" },
217+
/* stopAfterFirstMatch = */true)
218+
}
206219

207220
override fun getProtocol(): String {
208221
return "file"
@@ -217,18 +230,16 @@ class WslVirtualFileSystem: LocalFileSystemImpl() {
217230
}
218231

219232
fun getRealVirtualFile(file: VirtualFile): VirtualFile {
220-
lock.withLock {
221-
if (reentry == file) {
222-
throw Error("error")
223-
}
224-
reentry = file
225-
val symlkinkWsl = file.parents.find { it.isFromWSL() && this.getWslSymlinksProviders(file).isWslSymlink(it) }
226-
val relative = symlkinkWsl?.path?.let { file.path.replace(it, "") }
227-
val resolved = symlkinkWsl?.let { virtualFile -> this.resolveSymLink(virtualFile)?.let { this.findFileByPath(it) } }
228-
val r = relative?.let { resolved?.findFileByRelativePath(it) } ?: file
229-
reentry = null
230-
return r
231-
}
233+
val symlkinkWsl = file.parents.find { it.isFromWSL() && this.getWslSymlinksProviders(file).isWslSymlink(it) }
234+
val relative = symlkinkWsl?.path?.let { file.path.replace(it, "") }
235+
val resolved = symlkinkWsl?.let { virtualFile -> this.resolveSymLink(virtualFile)?.let { this.findFileByPath(it) } }
236+
val r = relative?.let { resolved?.findFileByRelativePath(it) } ?: file
237+
return r
238+
}
239+
240+
override fun getInputStream(vfile: VirtualFile): InputStream {
241+
val file = this.getRealVirtualFile(vfile)
242+
return super.getInputStream(file)
232243
}
233244

234245
override fun contentsToByteArray(vfile: VirtualFile): ByteArray {
@@ -244,20 +255,30 @@ class WslVirtualFileSystem: LocalFileSystemImpl() {
244255
override fun getAttributes(vfile: VirtualFile): FileAttributes? {
245256
val file = getRealVirtualFile(vfile)
246257
var attributes = super.getAttributes(file)
258+
247259
if (attributes != null && attributes.type == null && this.getWslSymlinksProviders(file).isWslSymlink(file)) {
248-
val resolved = this.resolveSymLink(file)?.let { this.findFileByPath(it) }
260+
val resolved = this.resolveSymLink(file)?.let { resPath ->
261+
return@let object : StubVirtualFile() {
262+
override fun getPath(): String {
263+
return resPath
264+
}
265+
266+
override fun getParent(): VirtualFile {
267+
return vfile.parent
268+
}
269+
}
270+
}
249271
if (resolved != null) {
250272
val resolvedAttrs = super.getAttributes(resolved)
251273
attributes = FileAttributes(resolvedAttrs?.isDirectory ?: false, false, true, attributes.isHidden, attributes.length, attributes.lastModified, attributes.isWritable, FileAttributes.CaseSensitivity.SENSITIVE)
252274
}
253-
254275
}
255276
return attributes
256277
}
257278

258279
override fun resolveSymLink(file: VirtualFile): String? {
259280
if (file.isFromWSL()) {
260-
return this.getWslSymlinksProviders(file).getWSLCanonicalPath(file);
281+
return this.getWslSymlinksProviders(file).getWSLCanonicalPath(file)
261282
}
262283
return super.resolveSymLink(file)
263284
}

0 commit comments

Comments
 (0)