Skip to content

readTQueueN #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open

readTQueueN #91

wants to merge 6 commits into from

Conversation

adrianmay
Copy link

This function retries until N items are available in a TQueue without removing them, and then returns them all at once. In some cases a small <> happens, but if the calling code is left to do this the performance cost is higher. It also becomes quite messy and is almost tantamount to reimplementing a queue in front of the queue.

@simonmar
Copy link
Member

If we want this function at all, it should come with some clearly documented caveats.

  1. There's no fairness in STM, so the transaction is likely to never succeed if there are other readers requiring fewer elements.
  2. The transaction is O(n) even if it retries due to the length call, so as a whole we get a quadratic cost: n writes, each of which triggers an O(n) transaction that retries.

I'm tempted to say let's not do this, given the pitfalls.

@adrianmay
Copy link
Author

adrianmay commented Apr 30, 2025

I need this functionality whether I like it or not, and I could implement it on top of the existing package, but I'd end up re-implementing lots of existing logic and I'd have worse performance issues. In my case N is usually 3 and would never exceed about 5, so I'm not really bothered about complexity in N.

I'm got rid of case 2 cos I realised that it's superfluous. Now I just retry instead, letting the items pile up on the write side.

lenTQueue didn't belong here and is gone.

I agree that your first point should be documented but it's hardly a show stopper - I can't think why people would have two threads consuming the same queue in different chunk sizes. If they did, then surely it would be obvious that the bigger chunk would rarely be obtained. This code does tolerate a mixture of readTQueueN and readTQueue from the same thread.

Moving to your second point, I could avoid calling length by counting writes in an Int in TQueue if you'd prefer, but personally I'm not sure it's worth the bother. There's this dogma that complexity is everything and duration is nothing, but in reality most of us are not optimising for infinite N.

It's not outlandish to want this functionality, and if the module doesn't provide it, then people have to do it themselves. If they don't fork the repo (if only to export the TQueue constructor) then the result will be worse on performance and maintainability. There's no flawless way to do it but to the best of my knowledge this is the least bad option, so why not save people the trouble?

Copy link
Member

@simonmar simonmar left a comment

Choose a reason for hiding this comment

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

OK with the documented caveats. Can we have a test please?

-- | 0 < read < N | retry | retry | case 4 |
-- +--------------+-----------+--------------- +-----------------+
-- | read >= N | . . . . . . . case 1 . . . . . . . . . |
-- +----=--------------------------------------------------------+

-- case 1a: More than N: splitAt N read -> put suffix in read and return prefix
-- case 1b: Exactly N: Reverse write into read, and return all of the old read
-- case 2: Move reverse write to read, retry
-- case 2: No longer exists
Copy link
Member

Choose a reason for hiding this comment

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

renumber the cases please

-- |Reads N values, blocking until enough are available
-- |Reads N values, blocking until enough are available.
-- This is likely never to return if another thread is
-- blocking on readTQueue. It has quadratic complexity
Copy link
Member

Choose a reason for hiding this comment

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

nit: markup identifiers e.g. 'readTQueue'

-- |Reads N values, blocking until enough are available.
-- This is likely never to return if another thread is
-- blocking on readTQueue. It has quadratic complexity
-- in n due to each write triggering readTQueueN to calculate
Copy link
Member

Choose a reason for hiding this comment

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

nit: N not n

Copy link
Author

Choose a reason for hiding this comment

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

There's a test in Stm066.hs. I made the other changes.

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.

2 participants