Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make puts thread-safe

I'm have a multithreaded program that prints to the console in hundreds of places. Unfortunately, instead of

Line 2
Line 1
Line 3

I get

Line2Line1

Line3

I am trying to make puts thread safe.


In Python (which I don't think has this problem, but suppose it did), I'd do

old_print = print

print_mutex = threading.Lock()

def print(*args, **kwargs):
    print_mutex.acquire()
    try:
        old_print(*args, **kwargs)
    finally:
        print_mutex.release()

I'm trying this in Ruby,

old_puts = puts

puts_mutex = Mutex.new

def puts(*args)
    puts_mutex.synchronize {
        old_puts(*args)
    }

But this doesn't work: "undefined method old_puts"


How can I make thread-safe (i.e. not print partial lines)?

like image 549
Paul Draper Avatar asked Dec 11 '15 16:12

Paul Draper


People also ask

How do you make a class thread-safe?

To make these classes thread-safe, you must prevent concurrent access to the internal state of an instance by more than one thread. Because Java was designed with threads in mind, the language provides the synchronized modifier, which does just that.

Is printf () thread-safe?

the standard C printf() and scanf() functions use stdio so they are thread-safe.

Is writing to stdout thread-safe?

A side note: std::cout is thread-safe Each character will atomically be written.


2 Answers

alias old_puts puts

or more modern way:

module MyKernel
  PutsMutex = Mutex.new
  def puts(*)
    PutsMutex.synchronize{super}
  end
end

module Kernel
  prepend MyKernel
end
like image 186
sawa Avatar answered Sep 20 '22 11:09

sawa


The reason for this behaviour is that puts internally calls the underlying write function twice - one for the actual value to be written, and one for the newline to be written. (Explained in Ruby's puts is not atomic)

Here's a hack to make puts call write exactly once: Append \n to the string you're writing. Here's what this looks like in my code:

# Threadsafe `puts` that outputs text and newline atomically
def safe_puts(msg)
    puts msg + "\n"
end

puts internally checks whether the object being written has a newline at the end, and only calls write again if that isn't true. Since we've changed the input to end with a newline, puts ends up making only one call to write.

like image 35
rohitpaulk Avatar answered Sep 20 '22 11:09

rohitpaulk