import Control.Concurrent
    ( newQSem,
      signalQSem,
      waitQSem,
      threadDelay,
      forkIO,
      newEmptyMVar,
      putMVar,
      takeMVar,
      QSem,
      MVar )
import Control.Monad ( replicateM_ )

worker :: QSem -> MVar String -> Int -> IO ()
worker q m n = do
  waitQSem q
  putMVar m $ "Worker " <> show n <> " has acquired the lock."
  threadDelay 2000000 
  signalQSem q
  putMVar m $ "Worker " <> show n <> " has released the lock."

main :: IO ()
main = do
  q <- newQSem 3
  m <- newEmptyMVar
  let workers = 5
      prints = 2 * workers
  mapM_ (forkIO . worker q m) [1 .. workers]
  replicateM_ prints $ takeMVar m >>= putStrLn