https://kotlinlang.org logo
Title
o

otakusenpai

10/19/2018, 3:22 AM
Is something wrong with my code here(doing java.io.File writing in a coroutine)
class AsyncLogger : AbstractLogger {

    constructor(filepath: String,name: String) : super() {
        var file = File(filepath)
        file.mkdirs()
        filename = "$filepath/$name"
        file = File(filename)
    }

    suspend fun log(toLog: Boolean,msg: String) = coroutineScope {
        GlobalScope.launch {
            if (toLog)
                writeToFile(msg)
        }
    }

    suspend fun log(toLog: Boolean,toFile: Boolean,msg: String) = coroutineScope {
        GlobalScope.launch {
            if(toLog)
                when(toFile) {
                    true -> writeToFile(msg)
                    false -> writeToFile(msg)
                }
        }
    }

    override suspend fun writeToSTDOUT(msg: String) =
            println(message = "[${Thread.currentThread().name}]: $msg")

    override suspend fun writeToFile(msg: String): Job = coroutineScope {
        GlobalScope.launch {
            val stream = RandomAccessFile(filename, "rw")
            val channel = stream.channel
            var lock: FileLock? = null
            try {
                lock = channel.tryLock()
            } catch (e: OverlappingFileLockException) {
                stream.close()
                channel.close()
            }
            stream.writeBytes(msg + '\n')
            lock?.release()
            stream.close()
            channel.close()
        }
    }

}
Getting this error;
Exception in thread "DefaultDispatcher-worker-2" java.io.IOException: Stream Closed
	at java.io.RandomAccessFile.writeBytes(Native Method)
	at java.io.RandomAccessFile.writeBytes(RandomAccessFile.java:1100)
	at com.github.otakusenpai.aghora.commonUtils.log.AsyncLogger$writeToFile$2$1.doResume(AsyncLogger.kt:50)
	at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:54)
	at kotlinx.coroutines.experimental.DispatchedTask$DefaultImpls.run(Dispatched.kt:168)
	at kotlinx.coroutines.experimental.DispatchedContinuation.run(Dispatched.kt:13)
	at kotlinx.coroutines.experimental.scheduling.Task.run(Tasks.kt:94)
	at kotlinx.coroutines.experimental.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:583)
	at kotlinx.coroutines.experimental.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
	at kotlinx.coroutines.experimental.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:729)
at here
stream.close()
n

Nikky

10/19/2018, 3:24 AM
m guess is that you are still doing work on the stream, but since you just launch and do not wait it closes it while still working on it.. where that code is.. i do no know on first glance
o

otakusenpai

10/19/2018, 3:25 AM
so should i do a runBlocking call ?
n

Nikky

10/19/2018, 3:26 AM
i have honestly no cle if that would be better
o

otakusenpai

10/19/2018, 3:26 AM
hmm thnx anyways
h

hallvard

10/19/2018, 6:11 AM
with
use {}
you don't have to bother about closing the streams yourself ...
o

otakusenpai

10/19/2018, 6:11 AM
hmm thnx
a

Alexander

10/22/2018, 2:06 PM
Think locking a file on every write operation is a bad idea. Such a writer can be implemented using an actor:
override suspend fun writeToFile(msg: String) = scope.actor<String> {
    for (msg in this) {
        stream.writeBytes("$msg\n")
    }
}
And inside
log
function you can just send message into the channel.
There can be single
log
function:
suspend fun log(msg: String, toLog: Boolean, toFile: Boolean = false)
Possibly it is worth to combine
toLog
&
toFile
arguments into an enum.
Coroutine
scope
can be passed via constructor, so you will be able to define the scope from the outside.