LibQueue
by DrDoom (Shalune on Kargath)
This is an open-source delayed-processing and buffer queue library developed for World of Warcraft
There are three functional concepts this library works with:
Queue: Allow you to place information on it for later retrieval
Standard queues use a FIFO buffer with rotational positioning, meaning no constant
clientside cleanup, therefore faster access. The queue is designed to handle any type
of data, even nil, so prior datachecking is recommended.
Timer: Timers execute a function every x seconds until stopped by the addon. These can be
executed and controlled as groups, allowing you to do multiple things at once, and turn
on or off processing of these timers en masse.
Delay: Delays execute a given function after x seconds, and do not repeat. Delays are cleared
automatically after they are processed. Delay objects allow you to extend processing
until something is done, and you can extend the time before it is executed as well. This
is great for events like BAG_UPDATE if you want to do an inventory scan.
Library functions are not embedded in created queue objects
CreateQueue
Arguments:
table QueueName Table object to store the queue
integer MaxSize Maximum size of the queue in elements
boolean AllowRecentDuplicates Allow duplicates of the most recent entry
boolean AllowAnyDuplicates Allow duplicates anywhere in the table
boolean IsStack Queue is processed like a stack (FILO)
Description:
Creates a queue, you must specify a maximum size. If given queue does not exist,
table will be created in the global environment
Default behavior is to allow any duplicates, if AllowAnyDuplicates is true, then
AllowRecentDuplicates will be ignored.
Example:
local MyQueue = {};
success = LibQueue:CreateQueue(MyQueue,500,true,false,false);
success = LibQueue:CreateQueue(MyStack,50,,true,true);
Returns:
true = Queue created successfully
false = Queue not created (invalid argument)
Put
Arguments:
table QueueName
any Value;
Description:
Adds a value to the end of the Queues internal positioning, buffer will be expanded
if needed, not to exceed the maximum size. If the queue is full, the data is discarded.
Example:
success = LibQueue:Put(MyQueue,"Hello");
success = LibQueue:Put(MyStack,123);
success = LibQueue:Put(MyQueue, {1,"2",3} );
Returns:
true = Value added successfully
false = Value discarded
Get
Arguments:
table QueueName
Description:
Extracts the next value from the table.
Example:
value = LibQueue:Get(MyQueue)
Returns:
nil = Queue is empty or value is nil
nonnil = Next value in line
Peek
Arguments:
table QueueName
boolean LastValue
Description:
Extracts the next value in line from the table without removing it.
If LastValue is specified, it will extract the last value added (has no effect on a stack).
Example:
nextvalue = LibQueue:Peek(MyQueue)
lastvalue = LibQueue:Peek(MyQueue,true)
lastvalue = LibQueue:Peek(MyStack)
lastvalue = LibQueue:Peek(MyStack,true)
Returns:
nil = Queue is empty or Value is nil
nonnil = Value requested
Size
Arguments:
table QueueName
Description:
Returns the size of the current Queue or Stack.
Example:
Size,BufferSize = LibQueue:Size(MyQueue)
Returns:
Size is the number of active elements in the buffer
BufferSize is the total number of elements in the buffer.
nil = Queue is not valid or has not been created
integer = Number of elements in the Queue, 0 = empty
Wipe
Arguments:
table QueueName
boolean ClearBuffer
Description:
Empties the queue and discards all information, will also optionally clean up the unused
buffer space
Example:
LibQueue:Wipe(MyQueue,false) Does not discard buffer data, no memory is freed up
LibQueue:Wipe(MyQueue,true) Discards the buffer, memory is freed
Returns:
Nothing
CreateTimer
Arguments:
string Name Name of the delay object to make
number Time Time interval to call Callback (seconds)
function Callback Function to call when delay has completed
any Data Data to pass to Callback function when called
AllowDuplicates Allow multiple timers with this name?
InCombat If set to false, timer will be paused in combat
Description:
Creates a timer, which calls Callback(Data); every Time seconds.
Default behavior is AllowDuplicates=true, AllowExtend=false,InCombat=true
Returns:
false = Delay not created
true = Delay created successfully or existing delay extended
Example:
function MyAddon_Func1(value)
print("Your lucky number is "..tostring(value));
end
success = LibQueue:CreateDelay("MyAddon_Delay1",5,MyAddon_Func1,10);
After 5 seconds, outputs: Your lucky number is 10
StopTimer
Arguments:
string Name Name of the Timer object
Description:
Pauses the timers on all Timer objects named Name
Returns:
Nothing
Example:
LibQueue:StopTimer("MyAddon_Timer");
StartTimer
Arguments:
string Name Name of the Timer object
Description:
Unpauses the timers on all Timer objects named Name
Returns:
Nothing
Example:
LibQueue:StartTimer("MyAddon_Timer");
ToggleTimer
Arguments:
string Name Name of the Timer object
Description:
Toggles the paused state of the timers on all Timer objects named Name
Returns:
Nothing
Example:
LibQueue:ToggleTimer("MyAddon_Timer");
ClearTimer
Arguments:
string Name Name of the Timer object
Description:
Removes all instances of the named timer.
Returns:
Nothing
Example:
LibQueue:ClearTimer("MyAddon_Timer");
CreateDelay
Arguments:
string Name Name of the delay object to make
number Time Time to delay processing (seconds)
function Callback Function to call when delay has completed
any Data Data to pass to Callback function when called
AllowDuplicates Allow multiple delays with this name?
AllowExtend Allow subsequent calls of this delay to extend the time?
InCombat If set to false, timer will be paused in combat
Description:
Creates a delay event, waits for given time and calls Callback(Data);
AllowDuplicates and AllowExtend will be ignored if AllowDuplicates is set to true.
Default behavior is AllowDuplicates=true, AllowExtend=false,InCombat=true
Remember that if an existing Delay is extended, all the original parameters are kept,
except the Time, which is replaced by the value you specified in this call.
Returns:
false = Delay not created
true = Delay created successfully or existing delay extended
Example:
function MyAddon_Func1(value)
print("Your lucky number is "..tostring(value));
end
success = LibQueue:CreateDelay("MyAddon_Delay1",5,MyAddon_Func1,10);
After 5 seconds, outputs: Your lucky number is 10
Supplemental Notes
* WHY? *
Why use timers and delays? Why not handle everything as it is given to me?
Several times, some events are called multiple times successively, and the data isn't available
immediately. BAG_UPDATE is a good example. In this case, you can use a delay with AllowExtend
to wait to process the inventory of a character until after all bags have been processed. This
reduces interface lag as well. Bag inventory is not available until the last call of BAG_UPDATE
has fired, and if you may end up scanning the entire inventory multiple times.
In general, handling large amounts of data or doing lots of things at once will create lots of
Interface lag. Using a timer, and adding a processing queue to your mod will decrease lag by
a noticeable amount if used properly, even in increments of 0.1 seconds. When WoW is parsing
scripts, it can't update the screen effectively. You may also use timers to ensure that large
or timeconsuming sections of code aren't run multiple times when they don't need to be.
The delay is also a great way to make sure the client loads fully before initializing,
this eliminates the loading lag that people with lots of addons inevitably experience.
* QUEUES vs STACKS *
Queues and Stacks are advanced data storage structures that let you process information in a
more organized manner, and are extremely helpful for sorting algorithms and other types of
advanced datahandling procedures. Below are pictorials to help you understand.
IN > QUEUE > OUT
12345 54321 12345
STACK < IN 12345
12345
STACK > OUT 54321
* HOW TO CREATE AN ACTION PROCESSING QUEUE *
Create a queue, and use a timer to process the queue, pulling each value out and acting on it.
Add data to the queue as tables, or string values corresponding to psuedo commands, which you
process later in the timer callback. You can even pass the Queue itself as data in the timer
function. Processing the queue at intervals such as 0.05 or 0.1 will help reduce lag.
This can also help eliminate redundant actions, especailly screen updates, which can
exponentially increase your lag.
* HOW TO PASS DATA EFFECTIVELY *
The data you put in a Timer, Delay, or Queue isn't fixed in stone unless you want it to be. If
you pass an existing value that you reference elsewhere, you can change the values in between
calls or before the event fires (like say, to cancel it from having an effect). A good idea to
pass multiple types of data at once is to pass a table as well.
Remember, as long as the WoW client has some global variable referencing a table or value, that
table or value will never be destroyed. You can pass pointers to existing tables into the timers
and the tables will still exist after the timer processes it.
* HOW TO USE MULTIPLE TIMERS/DELAYS EFFECTIVELY *
Timers or Delays can have multiples with the same name going at once. With this system, you
can Pause/Start/Toggle all samenamed timers at once, or ensure the same function is called
multiple times concurrently with different values. Remember that the function called does not
correlate to the name, however.
* NAMING IS IMPORTANT *
Remember also that Timers and Delays are stored internally, and that you should apply
modspecific naming conventions to identify your timers or delays.
Naming is also CASE SENSITIVE!
Queues are stored in your own global variables, and should have modspecific naming anyway so
you don't risk it getting overwritten by another mod.
Ex. Don't call it QueueB, call it MyAddonName_QueueB
* COMBAT *
Timers or Delays that have InCombat set to false will still tick during combat, but processing
will be delayed until combat ends.
* YOU'RE NOT THINKING 4TH DIMENSIONALLY! *
Don't be like Marty, plan your code execution. If you create two delays at the same time, and
the first one is extended, don't count on it having run already when the 2nd one fires.
Remember to be a responsible denizen of the 4th dimension, as well, bogging down the Queue
library with dozens of timers and delays will impact every other mod using it, combine what you
can.

