Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js and the Filesystem: is this a race condition?

I have the following code inside a class. (It's coffeescript-- and it's for a couchdb utility!-- but this is really a node.js question). I'm attempting to do things The Node Way, using Node 0.49, and that means using asynchronous calls for filesystem operations. At first, I was pulling my hair out because this.sentinel went to zero several times during the course of processing, so I know I'm doing something wrong there. But then I hit an even weirder issue: down in load_directory, see those console.log() calls? Watch when happens when I run this.

check_sentinel: ->
    @sentinel--
    if @sentinel == 0
        @emit('designDirLoaded', @object)

load_file: (rootdir, filename, object) ->
    @sentinel++
    fname = path.join(rootdir, filename)
    @manifest.push(fname)
    fs.readFile fname, (err, data) =>
        object[filename] = data
        @check_sentinel()

load_directory: (dirpath, object) ->
    @sentinel++
    fs.readdir dirpath, (err, files) =>
        for fname in files
            console.log("X1: ", fname)
            fs.stat path.join(dirpath, fname), (err, stats) =>
                console.log("X2: ", fname)
                if stats.isFile()
                    @load_file(dirpath, fname, object)
                if stats.isDirectory()
                    object[fname] = {}
                    @load_directory(path.join(dirpath, fname), object[fname])
        @check_sentinel()

Here's what I get:

X1:  memberByName.js
X1:  memberByClub.js
X2:  memberByClub.js
X2:  memberByClub.js

This is surreal, and it looks a lot like a race condition. "memberByName" gets passed to fs.stat(), which in turn passes "memberByClub" to load_file(), implying... what? That because load_file() returned immediately, it raced around and presented the next file name in the array to the function call? Or do I have some misunderstanding about the persistence of values in a given scope?

like image 499
Elf Sternberg Avatar asked Jul 10 '11 17:07

Elf Sternberg


People also ask

Does Nodejs have race conditions?

Yes, we can have race conditions in Node.

What is race condition example?

A simple example of a race condition is a light switch. In some homes, there are multiple light switches connected to a common ceiling light. When these types of circuits are used, the switch position becomes irrelevant. If the light is on, moving either switch from its current position turns the light off.

What data have a race condition?

A race condition occurs when two threads access a shared variable at the same time.

Is a data race a race condition?

A race condition occurs when the timing or order of events affects the correctness of a piece of code. A data race occurs when one thread accesses a mutable object while another thread is writing to it.


1 Answers

No, what you see is expected. One thing you have to remember is that fs.stat is asynchronous. So, the outer loop (for fname in files) will finish looping before any of the callbacks to fs.stat is called.

The reason why you see memberByClub.js twice is that you are using fname in the logging statement, but that variable is from the closure, which has changed by the time your callback to fs.stat is called.

You can wrap the inner loop with do (fname) => to get the correct logging statements, but I think you need to restructure your code to achieve what you are trying to do with the whole class.

load_directory: (dirpath, object) ->
    @sentinel++
    fs.readdir dirpath, (err, files) =>
        for fname in files
            do (fname) =>
                console.log("X1: ", fname)
                fs.stat path.join(dirpath, fname), (err, stats) =>
                    console.log("X2: ", fname)
                    if stats.isFile()
                         @load_file(dirpath, fname, object)
                    if stats.isDirectory()
                        object[fname] = {}
                        @load_directory(path.join(dirpath, fname), object[fname])
        @check_sentinel()
like image 167
Barum Rho Avatar answered Sep 28 '22 00:09

Barum Rho