Skip to content

Deadlock prevention - #381

Closed
lucasamorimca wants to merge 1 commit into
rails:masterfrom
lucasamorimca:thread-pool-for-io
Closed

Deadlock prevention #381
lucasamorimca wants to merge 1 commit into
rails:masterfrom
lucasamorimca:thread-pool-for-io

Conversation

@lucasamorimca

Copy link
Copy Markdown
Contributor

For more information: #376

cc @schneems @matthewd

@rails-bot

Copy link
Copy Markdown

r? @arthurnn

(@rails-bot has picked a reviewer for you, use r? to override)

Comment thread lib/sprockets/manifest.rb
return if gzip.cannot_compress?(environment.mime_types)

if File.exist?("#{target}.gz")
logger.debug "Skipping #{target}.gz, already exists"

@schneems schneems Sep 13, 2016

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no mutex around the logging in sprockets so logging from within a thread can result in garbled output. That's why originally it was logged outside of the pool.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for letting me know. I'll fix it later today.

Comment thread lib/sprockets/manifest.rb
logger.debug "Skipping #{target}, already exists"
compressor.execute if compressor
else
logger.info "Writing #{target}"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to run create_compressor after each time we log since it outputs to logs right away Otherwise since create_compressor immediately outputs a log, you would could get something in your output like

Writing #{target}.gz
Writing #{target}

or

"Skipping #{target}.gz, already exists"
"Skipping #{target}, already exists"

Which is backwards.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

schneems added a commit that referenced this pull request Sep 20, 2016
I started with #372 and made some style and behavior changes. The idea remains the same, that all writing to disk will happen via an exporter which gives us a place we can modify behavior from a gem.

The interface for an exporter is as follows:

An exporter is a class, a new instance will be initialized for each asset. It has access to:

- asset
- environment
- directory
- target

There is a `setup` method that can be used to create mutable state before the exporter is called.

A `skip?` method is run synchronously to determine if the exporter should execute or not. Since this is the only method guaranteed to run synchronously and since our logger is not threadsafe, we can do notifications in this method. The logger is passed into `skip?` to prevent people from accidentally storing a reference and trying to log when things are being called in different threads.

Finally a `call` method does the work of writing a file to disk. It takes no arguments.

Other changes? The top level lib/sprockets dir is really large so I moved the built in exporters to their own directory. Added documentation. Only allow registering of classes for exporters instead of procs. Added a zopfli exporter by default if you have the gem loaded before sprockets gets required. Switch back to using `compile` for the manifest interface. Use promises to chain asset compiles, this also handles #381. Fix tests to not use `rm -rf` with a system call in a few places
@schneems schneems mentioned this pull request Sep 20, 2016
@schneems

Copy link
Copy Markdown
Member

It doesn't look like calling Promise#wait! does what either of us thought it would ruby-concurrency/concurrent-ruby#575. Also I'm working in parallel on another PR that happens to touch this code for exporting assets.

@lucasamorimca

Copy link
Copy Markdown
Contributor Author

That sounds a little different from what we're doing here. In this code, we are not waiting for the chain of promises to finish up, we're calling wait! in each promise. Am I missing something?

@schneems

Copy link
Copy Markdown
Member

Sorry for stringing you along for so long. I needed to re-write some manifest logic that overlapped here and re-wrote this section. Thanks for this patch and for the original idea of limiting the threads for promises. I appreciate all your help even if this patch isn't being merged.

@schneems schneems closed this Oct 10, 2016
@lucasamorimca
lucasamorimca deleted the thread-pool-for-io branch October 10, 2016 16:23
@lucasamorimca

Copy link
Copy Markdown
Contributor Author

@schneems Thank you for all the feedback, let me know if you guys need anything.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants