Closed Bug 729057 (MTOS.File) Opened 12 years ago Closed 12 years ago

[OS.File] Threaded implementation

Categories

(Toolkit Graveyard :: OS.File, enhancement)

enhancement
Not set
normal

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: Yoric, Assigned: Yoric)

References

Details

(Keywords: main-thread-io, perf)

Attachments

(4 files)

The Efficient JS File API must be asynchronous. Due to lack of general OS-based support for asynchronous file IO (see bug 709736), this means that our main back-end will require one (or more) IO worker(s).
I am not sure we can easily have a single design that will work both as a library for the main thread and as a library for chrome worker threads. For the moment, I will concentrate on the first case.

General design:
- keep the IO worker thread(s) as stateless as possible, to simplify resource management;
- take advantage of finalization in the controller (i.e. the main thread);
- a IO worker thread can only be working on one operation at a time (i.e. no setTimeout + interleaving of IO operations on one single thread).


1. IO operations

The controller:
 - receives a request;
 - immediately returns a |Future|;
 - pushes the |Future| on its reply queue;
 - extracts from the request the file descriptor/handle and the low-level function call;
 - routes the request to the worker;
 - expects a response (note that workers handle only one message at a time, so unless we introduce setTimeout, responses must arrive in the same order as requests);
 - pop the first Future from the queue;
 - resolve/reject the Future.

The worker:
 - receives a request and a file descriptor/handle upon which to execute it;
 - executes the request;
 - posts the response.


2. Resource finalization

We wish to dispose of the resources associated to one file when:
 a. a |file.close| operation is called on the main thread;
 b. the object representing the file ceases being referenced.

Case a. is simple. We can perform the actual system call on either the main thread or the IO worker thread, this does not change the semantics.

Case b. requires IO worker threads to be stateless and takes advantage of CData finalization (see bug 720771).

API Client:
- Requests a File object from the Controller.
- Receives it asynchronously.
- Can call |close| on the file object or can lose the last reference to the file object, in which case the file object is finalized.

Controller:
- Receives a file creation request from the client.
- Transmits the request to the IO Worker.
- Receives a reply asynchronously.
- If the reply is a success, attach low-level |close|/|CloseHandle| operation as finalizer to the file descriptor/handle.
- Build a File object, containing as field the file descriptor/handle.

IO Worker:
- As above.

As the IO Worker holds no file-specific resource, no complex cross-thread resource-management scheme is required.
Depends on: JSScheduleAPI
OS: Mac OS X → All
Summary: Efficient JS File API - Threaded back-end → [OS.File] Threaded back-end
Severity: normal → enhancement
Depends on: 708984
No longer depends on: JSScheduleAPI
Hardware: x86 → All
(In reply to David Rajchenbach Teller [:Yoric] from comment #1)
> We wish to dispose of the resources associated to one file when:
>  a. a |file.close| operation is called on the main thread;
>  b. the object representing the file ceases being referenced.
> 
> Case a. is simple. We can perform the actual system call on either the main
> thread or the IO worker thread, this does not change the semantics.

You should not perform any file-related syscalls on the main thread. open() and close() can block on disk I/O just like read()/write() do. Anti-virus software, disk encryption software (e.g. Windows EFS), etc. can cause such problems.

> IO Worker:
> - As above.
> 
> As the IO Worker holds no file-specific resource, no complex cross-thread
> resource-management scheme is required.

I am not sure what you mean here. The IO worker holds the file descriptor so it can do the actual file I/O.

My experience with using the stream transport service (nsIStreamTransportService) in Necko is that you probably need a little coordination, especially to ensure that you don't close the file descriptor while the background thread has a read/write pending. This is also why you can't close the file descriptor on the main thread; the background thread may be blocked on a very long read/write kernel operation and you must wait for that read/write to complete before you close the file descriptor.
(In reply to Brian Smith (:bsmith) from comment #2)
> (In reply to David Rajchenbach Teller [:Yoric] from comment #1)
> > We wish to dispose of the resources associated to one file when:
> >  a. a |file.close| operation is called on the main thread;
> >  b. the object representing the file ceases being referenced.
> > 
> > Case a. is simple. We can perform the actual system call on either the main
> > thread or the IO worker thread, this does not change the semantics.
> 
> You should not perform any file-related syscalls on the main thread. open()
> and close() can block on disk I/O just like read()/write() do. Anti-virus
> software, disk encryption software (e.g. Windows EFS), etc. can cause such
> problems.

Good point.

> > IO Worker:
> > - As above.
> > 
> > As the IO Worker holds no file-specific resource, no complex cross-thread
> > resource-management scheme is required.
> 
> I am not sure what you mean here. The IO worker holds the file descriptor so
> it can do the actual file I/O.

I was essentially talking about garbage-collection and catching files that are not referenced anymore.

> My experience with using the stream transport service
> (nsIStreamTransportService) in Necko is that you probably need a little
> coordination, especially to ensure that you don't close the file descriptor
> while the background thread has a read/write pending. This is also why you
> can't close the file descriptor on the main thread; the background thread
> may be blocked on a very long read/write kernel operation and you must wait
> for that read/write to complete before you close the file descriptor.

For the moment, OS.File is aiming for a lower-level API, in which operations are simply enqueued. If you start a read/write, no other operation can take place on the (single) I/O thread until that read/write is complete.

Things will, of course, become more complicated once we have several I/O threads, but I have the feeling that, for file I/O, we will want to ensure that once a file is opened, it is always managed by the same worker.
Assignee: nobody → dteller
Attached patch Front-end glueSplinter Review
Attaching a few early sketches of what the threaded implementation might look like.
Summary: [OS.File] Threaded back-end → [OS.File] Threaded implementation
Alias: MTOS.File
Component: Networking: File → OS.File
Product: Core → Toolkit
This has basically landed. Closing the bug.
Status: NEW → RESOLVED
Closed: 12 years ago
Resolution: --- → FIXED
Product: Toolkit → Toolkit Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: