Skip to content

100% CPU usage with zeromq Push socket with 0 listeners

Created by: solatis

I originally submitted this as a StackOverflow question, but at this point I feel submitting this as an issue here is more appropriate.

I am currently in the process of implementing a communication pipeline between several processes using ZeroMQ, all using the Push/Pull mechanism. The pipeline starts with a 'ventilator' that generates tasks, and here is where my problem also starts: ZeroMQ seems to be using 100% CPU load when no workers are connected.

Here is the code in question, which attempts to send just one message:

module Main where

import System.ZMQ4.Monadic
import Data.ByteString.Char8 (pack)

main :: IO ()
main = do
     runZMQ $ do
            publisher <- socket Push
            bind publisher "tcp://*:10150"

            send publisher [] (pack "foo")

            close publisher

As you can see, this code is extremely simple and just attempts to send the message "foo" to any subscriber. I would expect this code to queue this message in the background, but instead it appears to get into a never-ending loop with the send command; when I strace the process, this is what I see happening:

poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
--- SIGVTALRM {si_signo=SIGVTALRM, si_code=SI_TIMER, si_pid=0, si_uid=0, si_value=0} ---
rt_sigreturn()                          = 1
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
--- SIGVTALRM {si_signo=SIGVTALRM, si_code=SI_TIMER, si_pid=0, si_uid=0, si_value=0} ---
rt_sigreturn()                          = 1

This is why I believe this might be a bug, or a problem in my code. There is an example in the zguide that is similar to what I'm trying to achieve: https://github.com/imatix/zguide/blob/master/examples/Haskell/taskvent.hs

In this example, they explicitly request user input to start sending (specifically, 'press enter when workers are ready') -- is this the way to work around this problem?

To elaborate, the following program (with a connected listener) works perfectly:

module Main where

import System.ZMQ4.Monadic
import Data.ByteString.Char8 (pack, unpack)
import Control.Applicative ((<$>))

main :: IO ()
main = do
     runZMQ $ do
            publisher <- socket Push
            receiver  <- socket Pull

            bind    publisher "tcp://*:10150"
            connect receiver "tcp://127.0.0.1:10150"

            send publisher [] (pack "foo")    
            message <- unpack <$> receive receiver

            liftIO $ putStrLn ("received data: " ++ message)