Embedded Library Woes

I have two addons whose directory structures are shown below. Both addons use the same [embedded] library, myLib-1.0. In myLib-1.0 there are two source files, the public services defined in myLib-1.0 and the private services defined in PrivateAPI.lua and only visible to the code in myLib-1.0.

Here’s the directory structure for FooAddon.

FooAddon
Libs\myLib-1.0
Libs\myLib-1.0\LibStub.lua
Libs\myLib-1.0\PrivateAPI.lua
Libs\myLib-1.0\myLib-1.0.lua

Foo.lua
FooAddon.toc

Here’s the directory structure for the BarAddon

BarAddon
Libs\myLib-1.0
Libs\myLib-1.0\LibStub.lua
Libs\myLib-1.0\PrivateAPI.lua
Libs\myLib-1.0\myLib-1.0.lua

Bar.lua
BarAddon.toc

In my current implementation, the FooAddon and BarAddon run fine when the other is not present. When both are loaded, the BarAddon (the first one loaded) is clobbered, I think, by the FooAddon because the former doesn’t work and the latter does. So, I’m wondering of this might be because the namespace of myLib is the same in both addons.

At any rate, it’s hard to explain, but any ideas as to how to approach this?

Cheers,

probably. the game only has one namespace. you can “hide” things by making them local but everything else is global so if you have a variable named myLib in both Foo and Bar then then Foo would define it first, then Bar would override it.

use LibStub for your library. if youve already done that then youve probably set it up wrong.

how does myLib-1.0.lua create the library?
how does Foo.lua assign the library?
how are you accessing the library?

it should look something like this

create/define

local libname = "ArkDewdrop"
local libversion = 30110
local lib = LibStub:NewLibrary( libname, libversion )

if not lib then
	return -- existing or newer library version already loaded
end

function lib.DoSomething( )
	-- do something
end

make available to mod

ArkInventory.Lib = { -- libraries live here
	Dewdrop = LibStub( "ArkDewdrop" )
}

calling library function from your mod

ArkInventory.Lib.Dewdrop.DoSomething(  )

if you want help youll probably need to upload and link your code

[quote=“Arkayenro, post:2, topic:1610075, username:Arkayenro-khazgoroth”]
if you have a variable named myLib in both Foo and Bar then then Foo would define it first, then Bar would override it
[/quote

I do use LibStub, and myLib is formed exactly as you’ve described. But I’m intrigued by this quote. If I may ask, how do non-programmers, users of Addons in which the libraries are embedded, get around this problem? In my game folder, more than a few AddOns incorporate one or more ACE libs. Why don’t they clobber each other?

and thanks for the advice,

p.s. Also, the embedded myLib libraries are instances of the same library. The code is exactly the same in both. Does that make a difference?

because libstub stops that from happening - unless the version it has “stubbed” is lower, then it allows it to be replaced/updated/clobbered with the newer version youre about to give it

you also have to have some sort of programming ability to write the library and/or the mod thats going to use it.

if you dont use libstub (or something like it) then yes it will keep clobbering the library table until the last mod with it loads - then everyone gets that version.

shouldnt. so if something is going wrong then it sounds like youre not loading it correctly in foo and bar.

youd have to post the code or the actual error message you are getting, preferably both.

Don’t think in terms of “instances”.

As the saying goes, “there can be only one”

If you have your library (same major/some minor) embedded in different addons then the first files containing the code with the highest minor will be the “only one” loaded and passed back to addons that later use LibStub:GetLib(major) (or just LibStub(major)) to retrieve the current (highest minor) version of the library.

LibStub is not passing back the “embedded” library code “instance”.

If you want to do that (want the library to hold addon sepcific data) then use the addon private table (or some other trickery) and get rid of LibStub.

Thank you both, Arkayenro and Fizz.

Arkayenro: As far as I can tell I’ve implemented the library and its clients as you’ve outlined. Still no joy. So, I guess it’s time for some code.

This is how I initialize the Library, WoWThreads.lua

local _, WoWThreads = ...

-- Initialize the library
local libName, version = "WoWThreads", 1
local thread = LibStub:NewLibrary( libName, version)
if not thread then return end

To test the library I have two simple Addons: ClientOne.lua and ClientTwo.lua. ClientOne is a simple regression test calling each thread API entry point under various circumstances. ClientTwo is a stress test which tries to break the code. Here’s ClientOne:

ClientOne.lua (ClientTwo.lua gets the library likewise).

local ClientOne = ...

local libName ="WoWThreads"
local thread = LibStub:GetLibrary( libName )
if not thread then 
    return 
end

local SIG_ALERT             = thread.SIG_ALERT
local SIG_JOIN_DATA_READY   = thread.SIG_JOIN_DATA_READY
local SIG_TERMINATE         = thread.SIG_TERMINATE
local SIG_METRICS           = thread.SIG_METRICS
local SIG_NONE_PENDING      = thread.SIG_NONE_PENDING

etc.,

That’s it. ClientOne and ClientTwo work exactly designed if no other addon using WoWThreads is running. Finally, if it’s useful, here’s the library’s directory structure. This directory and its files are contained in the WoWThreads library and the two client addons.

##---------------------------------------------
## WoW Thread Library Code
##---------------------------------------------
Libs\WoWThreads\LibStub.lua
Libs\WoWThreads\LibSources\Frames.lua 
Libs\WoWThreads\LibSources\Core.lua
Libs\WoWThreads\LibSources\EnUS_WoWThreads.lua
Libs\WoWThreads\LibSources\Dispatcher.lua
Libs\WoWThreads\LibSources\Manager.lua
Libs\WoWThreads\WoWThreads.lua

I am very grateful for your assistance with what must seem obvious to you. Nevertheless, the only stupid question is the one you don’t ask. So, there you go. :grinning:

Cheers

Get rid of that and make your library work without the WoWThreads table or, get rid of LibStub and JUST use the WoWThreads table as base table for your library.

Or, build your own versioning system that does both, LibStub is not that “fancy”.

WoWThreads used like this won’t allow your library to store “addon specific” information.

local thread = LibStub:GetLibrary( libName )

thread or whatever you call the variable will also be the same table (global) as every other addon that uses = LibStub:GetLibrary( libName )

So you can’t store addon specific data in thread either (unless you sepearate it locally).

But I do use LibStub in multiple AddOns, and only the last addon to be loaded runs – just as you described. My question is why? I’ve copied some code (as you suggested) that I think is relevant to this question that I hope you (or others) with more experience can examine and perhaps tell me what I’m missing.

Am I right in understanding that the use of WoWThreads (as shown in the code) is getting in the way, somehow?

Thanks to both of you, as always,

The code you posted doesn’t actually show any use of the WoWThreads table but the fact that you have it in your library .lua files shows that is could be.

If you think that the WoWThreads table is somehow different to each addon using the library and use the table as such in the library code (to save/use data specific to an addon using the library) then yes.

Just as the library code itself is the same for all addons (the first encountered with the highest minor) so will be the WoWThreads table it uses (the addon table from the same addon).

My guess is this is what’s at the heart of the:

problem.

that part looks fine so could you show us at least one library function you defined?

i want to double check you used thread:DoSomething() and not WoWThreads:DoSomething() in your library code

as Fizzlemizz says, this line local _, WoWThreads = ... doesnt belong in a library so im wondering why its there to begin with, and if you’ve used it incorrectly.

you also still havent said what the error you are getting when more than one mod with the library is loaded.

Given the latest input from you guys (which I very much appreciate), I’ve tried to refactor the code to reflect your comments. The new code almost works as explained below.

  1. I want to use LibStub if at all possible.
  2. I’ve written two addons, TestAddonOne and TestAddonTwo. Each of them include the LibStubbed embedded library, WoWThreads.
  3. I’ve restructured the “namespace” text at the beginning of each library file which, as Fizz pointed out, made no sense. I wanted WoWThreads to express the public API (i.e., thread:doSomething()) with each of the supporting (private API) files as subtables to WoWThreads. The code below is how I’ve tried to achieve these semantics. In their TOC load order, they are:

– FILE: EnUS_Locales.lua

local Addon, WowThreads = …
WoWThreads.Locales = {}
locales = WoWThreads.Locales

– FILE: Frames.lua

local Addon, WoWThreads = …
WoWThreads.Frames = {}
frame = WoWThreads.Frames

– FILE: Core.lua

local Addon, WoWThreads = …
WoWThreads.Core = {}
core = WoWThreads.Core

– FILE: Dispatcher.lua

local Addon, WoWThreads = …
WoWThreads.Dispatcher = {}
dispatch = WoWThreads.Dispatcher

– FILE: Manager.lua

local Addon, WoWThreads = …
WoWThreads.Manager = {}
mgr = WoWThreads.Manager

– FILE: WoWThreads.lua

local Addon, WowThreads = …
local libName = “WoWThreads”, 1
local version = 1
local thread = LibStub:NewLibrary( libName, version)
if not thread then return end

Like before, when loaded together, neither passes the regression tests. However, when loaded alone, they run to completion successfully. So, my goal in all this is to get the “namespace” tables (for lack of a better term) such that addons that use WoWThreads won’t tromp all over each other.

Using the namespace table in the addons is fine. Using the namespace table in your library .lua files is not.

Move your library code into a standalone addon of its own and then use it the two addons without embedding. If it can’t do that then the library is not structured correctly.

This is essentially the same effect that LibStub will have on multiple addons with the same library embedded. You will end up with the one global version of the library used by all addons. Nothing will be a separate “instance” including the namespace table.

In other words, convert it to a standalone, shared library accessed through a dependency clause in the Addon’s TOC?

Ironically, I originally structured it that way when I first wrote this thing and it seemed to work fine.

Thanks,

You can use a TOC dependency to make sure the library loads before the addon loads but if you don’t use (get or whatever gives you the table with the library functions) the library in the addon until after the PLAYER_LOGIN event has fired then it will have already loaded along with all the other non-LOD addons.

If it worked fine as a standalone when it was used with multiple addons then it should have work when embedded.

Did the orignal use the namespace table or was it just some functions that did stuff and hand any results back to the addon for future use/saving?

Putting into a standalone addon is just for showing how it works. Even with LibStub you effectively and up with the library as a single standalone addon. This seems to be the lightbulb you are missing.

I guess another example would be to just remove (comment out of the .toc file) the embedded library .lua files from one of the two addons.

You should be able to access the library in both but it should error for the same reasons. It might just be more intuative to understand why.

post the library code, at least enough that we can see what youre doing. it’s obvious theres an issue with it but no one is going to know for sure until we see it.

and you still wont mention the error(s) youre getting. that would help immensely.

it could just be that what youre building wont fit into the library structure that were all used to, but again, no code, no error, no real idea.

Alas, I’m currently out of town. When I return rather than post the lib’s code, I’ll post a link to project’s github directory.

Just a quick note: there are no error messages because in this case, when the second addon loads (and clobbers the first), one or more threads of the first addon are caused to cease execution without error, or get suspended in a yield loop. My gazillion asserts and state-checks catch none of this. Such is the nature of multithreading.

Finally, this package originally ran quite nicely as a shared library (an addon in its own right). All this smoke and fire arose because I wanted to see if I could provide an embedded lib solution. As that now seems impossible, I’ve more or less given up and restored the package as a [LibStubbed] shared, stand-alone library. And it’s working perfectly.

I am really grateful for the insights both of you have provided.

Cheers,

The question here is “How did you change the standalone library when you added LibStub”

The change shouldn’t be big, eg.

Standalone library

WoWThreadsLib = {} -- create a global table to "hold" the library code
function WoWThreadsLib:DoThingOne()
    -- do something
end
function WoWThreadsLib:DoThingTwo(arg1)
    -- do something with arg1
end

Embedded library

local Major = "WoWThreads_1" -- major version ie. v1
local Minor = 1 -- minor version ie. this will be v1.1
local WoWThreadsLib = LibStub:NewLibrary(Major, Minor) -- get a table from LibStub to "hold" the library code
function WoWThreadsLib:DoThingOne()
    -- do something
end
function WoWThreadsLib:DoThingTwo(arg1)
    -- do something with arg1
end

No

local Addon, WowThreads = …

In either version, because the WowThreads table would end up being shared across all addons that use the library (in essence making it a global).

Here are entry points to WoWThread’s public API, including the header stuff.

I did nothing special to get this to work (the LibStubbed version) as a standalone, shared library (i.e., it passes the regression tests when two or more addons call the services of the API).

local _, WoWThreads = ...

-- Initialize the library
local libName = "WoWThreads", 1
local version = 1

local thread = LibStub:NewLibrary( libName, version)
if not thread then return end

thread.SIG_ALERT            = dispatch.SIG_ALERT
thread.SIG_JOIN_DATA_READY  = dispatch.SIG_JOIN_DATA_READY
thread.SIG_TERMINATE        = dispatch.SIG_TERMINATE
thread.SIG_METRICS          = dispatch.SIG_METRICS
thread.SIG_NONE_PENDING     = dispatch.SIG_NONE_PENDING

-- RETURNS: (handle) thread_h, result
function thread:create( ticks, func, ... )
 
-- RETURNS: void
function thread:delay( ticks )

-- RETURNS; void
function thread:yield()

-- RETURNS: (number) threadId, result
function thread:getId( thread_h )

-- RETURNS: (handle) thread_h, (number) threadId
function thread:self()

-- RETURNS: (boolean) true if equal, result
function thread:areEqual( th1, th2 )

-- RETURNS: (handle) parent_h, result
function thread:getParent( thread_h )

-- RETURNS: (table) childThreads, result
function thread:getChildThreads( thread_h )
 
-- RETURNS: (string) state ( = "completed", "suspended", "queued", ), result.
function thread:getState( thread_h )

-----------------------------------------------------------
--                   SIGNAL FUNCTIONS                      -
-----------------------------------------------------------

-- RETURNS: (string) signalName. result
function thread:getSignalName( signal )

-- RETURNS: result. 
function thread:sendSignal( thread_h, signal )

-- RETURNS: (number) signal, (handle) sender_h
function thread:getSignal()

-------------------------------------------------------------------
--                  UTILITIES
-------------------------------------------------------------------
function thread:prefix( stackTrace )
function thread:print( ... )
function thread:printx( ... )
function thread:setResult( errMsg, stackTrace )
function thread:postResult( result )

A bunch of function declarations with no code is pretty useless.
No clue as to where dispatch is comming from in the above code but, guessing from previous posts, dispatch is something specific to the addon not the library then, needless to say, When addon 1 loads, thread.SIG_ALERT will contain dispatch.SIG_ALERT from addon 1. When addon 2 loads, thread.SIG_ALERT will contain dispatch.SIG_ALERT from addon 2

When either addon requests thread.SIG_ALERT from the library, it will get the one from Addon 2 because the one from Addon 1 has been overwritten.

Combine that with still no idea why the library contains

local _, WoWThreads = ...

But any use of the WoWThreads table (standalone or embedded) will cause the same condition as above.

That’s all I can do. Your top secret project is obviously way above my pay grade to see anything more useful.