guzba / mummy

An HTTP and WebSocket server for Nim that returns to the ancient ways of threads.
MIT License
274 stars 10 forks source link

Unable to use a 'ref' object in a route handler? #115

Closed matkuki closed 6 months ago

matkuki commented 6 months ago

Hi,

This code uses a global ref object in the index_handler:

import
    mummy,
    mummy/routers

type
    TestRef = ref object
        name: string

var
    new_ref = new(TestRef)
new_ref.name = "<div>Hello World</div>"

proc index_handler(request: Request) =
    var headers: HttpHeaders
    headers["Content-Type"] = "text/html"
    let content: string = new_ref.name # <- HERE IS THE PROBLEM
    request.respond(200, headers, $content)

proc main() =
    var router: Router
    router.get("/", index_handler)

    let
        server = newServer(router)
        ip_address = "0.0.0.0"
    echo "Serving on http://localhost:8080"
    server.serve(Port(8080), address=ip_address)

if is_main_module:
    main()

which in turn throws this error:

mummy_test.nim(21, 11) Error: type mismatch                                   
Expression: get(router, "/", index_handler)                                                    
  [1] router: Router                                                                           
  [2] "/": string                                                                              
  [3] index_handler: proc (request: Request)                                                   

Expected one of (first mismatch at [position]):                                                
[3] proc get(router: var Router; route: string | static string;                                
         handler: RequestHandler)

This is a contrived example, in my original code I use the tim library which uses a ref object to generate HTML templates, but it's the same problem.

Is there a solution for this?

guzba commented 6 months ago

Nim's ref object is not thread safe so it must be used with extreme care if threads are being used (almost certainly going to be broken if global).

I am sorry that I cannot explain super deeply here, it is a deep topic, but you'll need to ensure all ref objects are only ever instantiated and used in one thread (a handler call tree). This is because the reference count is not thread-safe or atomic. Some searching about Nim ref object and threads and --mm:arc/orc will find some discussion on the Nim forums.

guzba commented 6 months ago

(I should also note that the error in your post does not appear to be related to the area of the code you're highlighting.)

matkuki commented 6 months ago

Thanks.

I should also note that the error in your post does not appear to be related to the area of the code you're highlighting.

Correct, the problem is the indicated line, the error is for the router.get("/", index_handler) call. I just wanted to highlight that I had no idea what was wrong for quite some time, as the error message does not indicate where the problem is. In the original code I use the tim library, which I didn't know returned a ref object for generating HTML code, so it was weird as I just changed one line and it stopped working:

proc index_handler(request: Request) =
    var headers: HttpHeaders
    headers["Content-Type"] = "text/html"
    let content: string = timl.render("index")
    request.respond(200, headers, $content)

So I guess tim in not suited to for use with mummy.