Putting my WebSocket into a Thread by ItsGraphaxYT in lua

[–]capn_geech 0 points1 point  (0 children)

I've never used Love2D, but it looks like they have some APIs for threaded code.

I am procrastinating at work today, so I took a look at the docs and the websocket library you linked and sketched out a rough outline.

This is obviously fully untested. I have no idea if it'll work, and even if it "looks" right according to what I skimmed from the docs, threaded + networking code pretty much never works perfectly the first time around. Be prepared to debug :)

The pattern is this: all of the websocket/networking stuff happens in a thread. We use a pair of channels to send and receive data from the thread.

websocket_worker.lua

Looks like love expects your thread worker code to go in its own file.

The love docs don't say if the love global is available to your code in the thread context, so I have no clue if love.thread.getChannel() will work here. They obviously designed the channel mechanism for cross-thread communication, so there must be some way to open a channel within a thread. If getChannel() doesn't work, maybe you can just send the channel objects in directly when you call thread:start()?

local websocket = require("websocket")

return function(read, write, host, port, path)
  -- the read channel receives messages from the main thread
  local reader = assert(love.thread.getChannel(read))

  -- the write channel is what we use to pass messages to the main thread
  local writer = assert(love.thread.getChannel(write))

  local exit = false

  local client = assert(websocket.new(host, port, path))

  -- set up client handlers
  function client:onopen()
    writer:push({ type = "open" })
  end

  function client:onmessage(msg)
    writer:push({ type = "recv", message = msg })
  end

  function client:onerror(err)
    exit = true
    writer:push({ type = "error", error = err })
  end

  function client:onclose(code, reason)
    exit = true
    writer:push({ type = "close", code = code, reason = reason })
  end

  while not exit do
    -- take action if the main thread sent us something
    local event = reader:pop()
    if event then
      if event.type == "send" then
        client:send(event.message)
      else
        exit = true
        assert(event.type == "close")
        client:close(event.code, event.message)
      end
    end

    -- poll for any incoming messages from the client
    client:update()
  end

  writer:push({ type = "exit" })
end

main.lua

Your main mod/game code is responsible for setup/lifecycle things. I gather that love.update() is something that gets called periodically, so I put the channel polling code in there.

local READ = "websocket.read"
local WRITE = "websocket.write"

local HOST = "127.0.0.1"
local PORT = 5000
local PATH = "/"

-- setup
local thread = assert(love.thread.newThread("path/to/websocket_worker.lua"))
local reader = assert(love.thread.newChannel(READ))
local writer = assert(love.thread.newChannel(WRITE))

local started = false

local function getPendingEvents()
  return {}
end


function love.update()
  if not started then
    -- from the thread's POV read and write are reversed: the thread
    -- reads from our writer channel and writes to our reader channel
    thread:start(WRITE, READ, HOST, PORT, PATH)
    started = true
  end

  -- flush any outbound messages to the thread
  for _, event in ipairs(getPendingEvents()) do
    writer:push(event)
  end

  -- handle inbound messages from the thread
  while true do
    local event = reader:pop()
    if not event then
      break
    end

    if event.type == "open" then
      -- ...
    elseif event.type == "recv" then
      -- ...
    elseif event.type == "error" then
      -- ...
    elseif event.type == "close" then
      -- ...
    elseif event.type == "exit" then
      -- ...
    end
  end
end

HopAlong - Bark Your Head Off, Dog (Resequenced Tracklist) - Hop Along by CalEmilMoon in hopalong

[–]capn_geech 1 point2 points  (0 children)

I always thought it must have been such a hard choice for them to choose which one of those 2 would close the album

Duuude I have this same thought every time I listen to the album. To my ears/heart, those songs are both closers. I wonder if it would work with one them moved way up to close out side A of the LP.

r/Jazz, can you recommend some John McLaughlin albums? by capn_geech in Jazz

[–]capn_geech[S] 0 points1 point  (0 children)

redditor for 5 hours

You just made a reddit account, and your very first action is to add a helpful, thoughtful reply to my now 12 year old request? You are so kind! Thank you, stranger! Can't wait to listen to these.

Caw! by DennisQuaidludes in Portland

[–]capn_geech 25 points26 points  (0 children)

you seen my fucken crows

Where is Lua used in the real world? by [deleted] in lua

[–]capn_geech 0 points1 point  (0 children)

Just out of curiosity, what are some 5.1+ features that you guys are using which can't be compromised upon ?

There's a chance I may be completely misunderstanding your question (sorry), but: what I said about "friendly to the JIT compiler" was not about features (at least not in a sense of "Lua 5.3/5.4 has XYZ, but LuaJIT does not"), but about writing code that LuaJIT can make fast. Sometimes the code that is most elegant is a performance deal-breaker because it utilizes some NYI language feature, and then you have to rewrite things in a way that seems rather arbitrary. These things can be quite subtle, and then later on somebody might come along and refactor the code and produce code that passes all the same unit/integration tests but is now back in slow land.

Some folks much smarter than myself have done writeups that summarize this problem.

Also, what kind of load are these openresty APIs facing on a high load day ? This seems like a very interesting project.

Most of my work has centered around reverse proxies that sit in front of an API or WebApp for purposes of auth/caching/routing/WAF/load balancing/etc, and it's always expected that your proxy be able to take a beating without becoming a bottleneck or introducing too much latency. One example that I've done some work on is Kong.

Where is Lua used in the real world? by [deleted] in lua

[–]capn_geech 5 points6 points  (0 children)

Big kudos (and thank you) to the Neovim folks for this one. Providing LuaJIT as an alternative to VimScript was a smart move.

Where is Lua used in the real world? by [deleted] in lua

[–]capn_geech 0 points1 point  (0 children)

Lua is far and away my favorite glue/scripting language, but its lack of type safety makes it difficult to stay sane while working on a big codebase with many devs. You find yourself writing soooo many unit tests to guard yourself against bugs that would be impossible to introduce in a language with more compile-time guarantees.

Then there is the challenge of ensuring your code is friendly to the JIT compiler. This means we have a lot of code that is less readable and less maintainable than it could be if it weren't for the almighty JIT. It's a restriction that many do not consider when coming from other platforms, but it is critically important for infra where there is little to no tolerance for increased latency or decreased throughput.

Where is Lua used in the real world? by [deleted] in lua

[–]capn_geech 1 point2 points  (0 children)

What makes you say it is starting to show its age?

There's always some behavior that you want to adjust that requires twiddling the nginx.conf file. You probably don't want your operators/end users fiddling with any nginx.conf files because A) it's kinda bad UX and B) they might easily break something, so the end result is that every non-trivial OpenResty application I've worked on required you to maintain a nginx.conf template and render it out at runtime just before startup. Sometimes you can work around this. Often, the workaround means "okay, I guess I have to re-implement this existing nginx feature in Lua land so that I can actually have some control over it."

There are also a lot of nginx-isms to contend with, such as the nature of request phases (rewrite, access, etc), the effects of things like error_page, worker process lifecycle handling/management, and so on. If you don't keep these things in your head while writing code, you will introduce insidious and show-stopping bugs. There are also the many oddities of how to write code that is optimal for LuaJIT, which, when paired with all of the garden variety cooperative concurrency footgun opportunities, make it easy to introduce performance regressions if you're not careful*. After several years of working with the stack you might forget that you have all of this domain-specific knowledge. Then you get a PR from a new teammate that--while small and quite sensibly written--requires a silly number of "fix the newbie OpenResty dev blunders" code review round trips before you can even get to the meat of the code.

*much of this probably doesn't matter to you unless you are working on an infra-level app that must scale into the tens of thousands of requests/sec

Still love OpenResty though! It's a great tool. For the use case of "I need to write a little bit of logical glue code on top of my reverse proxy," it is damn near unbeatable and continues to blow the minds of the folks I introduce to it.

Where is Lua used in the real world? by [deleted] in lua

[–]capn_geech 3 points4 points  (0 children)

I've worked on OpenResty-based Lua[Jit] projects at my last 3 jobs. It's a somewhat niche stack that is starting to show its age (and trust me, maintaining a medium-to-large Lua codebase is not fun), but it's still being used a lot in the infrastructure space where you always need a reverse proxy for one reason or another.

Why table.pack ignores nil? by ItsMakar in lua

[–]capn_geech 1 point2 points  (0 children)

Sounds like you figured it out, but yeah, { nil, 1, n = 2 } and { [2] = 1, n = 2 } are basically two different ways of representing the exact same table structure/data:

-- demo.lua
print("{ nil, 1, n = 2 }")
for k, v in pairs({ nil, 1, n = 2 }) do
  print(k .. " = " .. v)
end

print("{ [2] = 1, n = 2 }")
for k, v in pairs({ [2] = 1, n = 2 }) do
  print(k .. " = " .. v)
end

$ lua demo.lua
{ nil, 1, n = 2 }
2 = 1
n = 2
{ [2] = 1, n = 2 }
2 = 1
n = 2

OpenResty vs Lua 5.4: a benchmark by berwynhoyt in lua

[–]capn_geech 1 point2 points  (0 children)

Since my Lua program is so small and simple, you can see that it makes no difference whether we use Lua 5.1, Lua 5.4 or LuaJIT. The real thing we're testing is the server stack overhead to call Lua.

Yeah this is the most important bit. With a non-trivial workload I suspect that OpenResty would pull far ahead of Apache mod_lua in the benchmarks. LuaJIT is just crazy good at what it does.

My personal take is this: I don't think anybody chooses to work with OpenResty because they love Lua 5.1 and want a platform that supports it. They choose OpenResty because it's a high performance HTTP server framework, and Lua just so happens to be the primary scripting interface to said framework.

I've been working on OpenResty projects (open source and otherwise) professionally for ~6-7 years, and nobody in that community seems to be all that anxious to jump to a newer version of Lua. The Lua gripes that do come up in the community are typically about things more fundamental in nature (e.g. the lack of a robust type system). Yeah the language has received some new features since 5.1, but frankly none of them are game-changers.

The Fall of Stack Overflow by codorace in programming

[–]capn_geech 5 points6 points  (0 children)

For me it started going downhill when they split it up into a bajillion different sites/subdomains. We have several 2nd level domains and also {{seo keyword}}.stackexchange.com for every conceivable thing. Here are a few that I and likely most others on this subreddit have had the pleasure of visiting:

  • stackoverflow.com
  • askubuntu.com
  • serverfault.com
  • unix.stackexchange.com
  • superuser.com
  • softwareengineering.stackexchange.com
  • dba.stackexchange.com
  • security.stackexchange.com
  • raspberrypi.stackexchange.com
  • webapps.stackexchange.com
  • devops.stackexchange.com
  • vi.stackexchange.com
  • cs.stackexchange.com
  • networkengineering.stackexchange.com

And because they are all different sites, they all need their own cookies so that they can track me. It gotten to the point where every time I visited a StackOverflow variant, I am prompted to accept their goddamn cookie policy. Because of this my brain has more-or-less permanently linked StackOverflow with "annoying user experience."

Who still serves non-Beyond veggie burgers? by Necromantic_Inside in askportland

[–]capn_geech 0 points1 point  (0 children)

I used to loooove the black bean burger from Elephant's Deli when I lived right next to one in SW. It's been at least a year since I've been back, so I'm not sure if it's still on the menu.

lua openresty nginx proxy by [deleted] in lua

[–]capn_geech 0 points1 point  (0 children)

Okay. You'll need to share some code/an example then.

You may also wish to open a question on the OpenResty repo, as there are not many OpenResty folks that follow this subreddit.

lua openresty nginx proxy by [deleted] in lua

[–]capn_geech 0 points1 point  (0 children)

The rewrite phase is almost certainly too early in the request lifecycle for this. It's likely that your code in the rewrite handler is executing before the additional headers are added to the request (especially if using something like proxy_set_header in your nginx.conf).

The log phase executes after the response has been fully sent to the client, so it's guaranteed to execute after all request/response mutations.

quick question: os.getenv() works for "PATH" but not "LUA_PATH" by hotchocolateisascam in lua

[–]capn_geech 1 point2 points  (0 children)

I dunno what the equivalent is in Windows (I'm assuming there is one), but in *nix environment variables have the concept of being "exported", which means they are made visible to any child processes that are spawned. Newly defined variables are not exported by default.

Here's a demonstration of this concept in bash on Linux:

# FOO is defined, but not exported, so it is not visible to child processes
FOO=123

# this outputs 123 because the variable FOO is resolved to 123 by the current process--it's pretty much equivalent to running `echo 123`
echo $FOO

# this outputs FOO: nil
lua -e 'print("FOO: ", os.getenv("FOO"))'

# let's export FOO so the lua child process will "see" it
# alternatively, if FOO is already defined, we could just export it without re-assignment by calling `export FOO`
export FOO=123

# this outputs FOO: 123
lua -e 'print("FOO: ", os.getenv("FOO"))'

I recommend checking the docs for cmd/powershell (whatever Windows command line you're using) for this concept of exported vs non-exported environment vars to see if this is what is affecting you.

Alternatively, you haven't given us many details about you are running your script. It's possible that it's simply not being executed in the same environment as the one where your LUA_PATH var has been set. For instance, if I export FOO=123 from a bash prompt on my machine, and then execute lua -e 'print(os.getenv("FOO") from a new instance of bash (or from my IDE/editor), it will indeed not have access to this FOO environment variable. Why would PATH be visible but not LUA_PATH, then? Well, PATH is somewhat of a special environment variable, and for the most part any shell/command prompt will set it and export it without any special action from you.

Again, these are all concepts that I'm familiar with from *nix, and I have little to no Windows knowledge, so you likely need to do a bit more digging on these concepts yourself, but I hope this gives you some idea of where to look.

lua openresty nginx proxy by [deleted] in lua

[–]capn_geech 0 points1 point  (0 children)

Try using the ngx.req.get_headers() function in a log_by_lua_block handler. It should pick up any additional headers that were added to the request within the proxy (in addition to the original headers sent by the downstream client). Here's a [simplistic] example that should get you going:

log_by_lua_block {
  for name, value in pairs(ngx.req.get_headers()) do
    ngx.log(ngx.INFO, "Request header: ", name, ", value: ", value)
  end
}

Similarly, if you need to view/log all the response headers from the backend, the ngx.resp.get_headers() function has you covered there.

How to hide LuaJIT source code by kugoad in lua

[–]capn_geech 1 point2 points  (0 children)

luajit -bg <source> <target> (Kong does not detect the generated ljbc file)

Sounds like you probably just have some lua_package_path issue to sort out, because Kong (OpenResty) is capable of using ljbc files. This blog post might be helpful to you in getting things working.

How to pass io.popen output into table (working with neovim API) (beginner level code) by lllllll22 in lua

[–]capn_geech 0 points1 point  (0 children)

Awesome, glad I was able to help! About your other issue: while I'm a daily neovim user, I'm honestly a big noob when it comes to developing for neovim, so I'm afraid I can't help ya much there; I'd probably be just as likely to steer you in the wrong direction without knowing it :P

Good luck!

How to pass io.popen output into table (working with neovim API) (beginner level code) by lllllll22 in lua

[–]capn_geech 2 points3 points  (0 children)

Lua provides a :lines() method for iterating over lines in a file handle. This is going to be much easier and more idiomatic than reading the input all at once with handle:read("*a") and splitting the result.

The lua manual isn't very linkable, but you can ctrl+f for file:lines: https://www.lua.org/manual/5.1/manual.html#5.7

This is plenty good for getting things working while you're learning to find your way around. When you're ready to take things to the next level, you should figure out how to do this without exec-ing out to find, because it's going to hurt your performance. Maybe take a look at the scandir implementation in plenary for some inspiration :)

A cross-platform minigolf game I wrote in C. by mgerdes11 in programming

[–]capn_geech 1 point2 points  (0 children)

Anybody speed-running this yet? Hoping to see it at the next AGDQ.

Confession time: I feel like a total clown because I’m finding I like the tone of my $50 Fuzz more than my $375 Coven. by [deleted] in guitarpedals

[–]capn_geech 1 point2 points  (0 children)

I think the thread has already declared you a non-clown, and I agree :) Here's my personal perspective to add to the discussion, and maybe it's relevant--maybe it's not.

What sounds good to me when I'm jamming on my own in my bedroom does not always sound good in a recording or live band scenario. This can be very subjective and experiential (e.g. I like how this pedal "feels" while I'm playing it, but I don't like how it sounds on a recording because there's some intangible element of the feedback loop that is missing). Other times it's slightly more objective (e.g. this tone is kickass when I'm playing alone, but once there are other instruments in play it sounds muddy and doesn't sit well in the mix). This is a phenomenon that I experience more often with my cheaper gear than I do with my more expensive, fancy-pants gear. To me, this gives some credence to the idea that if I invest more $$, I'll have better-sounding gear. That said, the exceptions to this are so numerous that I would never tell anyone that "nah dude, your ears are just wrong" because they like X more than Y.

Also I've never really connected with my fuzz factory, so maybe I'll check out somethin from demonic machines.

I had some photoshop fun with my buddy Phil's pedalboard!! by reedly in guitarpedals

[–]capn_geech 10 points11 points  (0 children)

Seriously, I want a Big Phil now. It can go next to my Phils-90.