change in behaviour on android

the following code:

fn processLoop() !void {
    try util.print("new Thread started..\n", .{});

    const io = util.io_global;
    const addr: net.IpAddress = .{.ip4 = .unspecified(port)};
    var serv = try addr.listen(io, .{.reuse_address = true});
    defer serv.deinit(io);


    // protocol: base64len*base64*

    while (true) {
        var connection = try serv.accept(io);
        defer connection.close(io);

        var size_buf: [4096]u8 = undefined;
        var size: usize = undefined;


        while(true){
            var stream_reader = connection.reader(io, &size_buf);
            const a = stream_reader.interface.takeDelimiter('*') catch break;
            if (a) |msg| {
                size = try std.fmt.parseInt(usize, msg, 10);
                try util.print("size: {d}\n",.{size});
                read_buf_size = size;
                break;
            } else break;

        }
        try util.print("after first while\n",.{});



        var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
        defer arena.deinit();

        const read_buf = try arena.allocator().alloc(u8, read_buf_size + 1);
        try util.print("after readbuf alloc\n",.{});

        while (true) {
            try util.print("in second while\n",.{});

            var stream_reader = connection.reader(io, read_buf);
            const a = stream_reader.interface.takeDelimiter('*') catch |err| {
                try util.print("ERROR: {}\n", .{err});
                break;
            };
            try util.print("after second delimiter\n",.{});
            try util.print("\n read buffer: {any} \n", .{read_buf});

            if (a) |msg| {
                try util.print("\n message: {s} \n", .{msg});
                try processMessage(msg);
                try util.print("\n Lua Script Exec End \n", .{});
            } else break;
        }
    }

}

works perfectly fine on windows but on android it doesnt enter the last if statement as ā€˜a’ is null. the client basically sends something like this:

    try writer.interface.writeAll(base64len+'*');
    try writer.interface.writeAll(base64+'*');

i am sorry for my buggy/weird code what might be the problem here? is it timing issue?

Can you provide the outputs you are comparing?

this is the windows output:

new Thread started..
size: 477
after first while
after readbuf alloc
in second while
after second delimiter

read buffer: {88, 88, 88, 88 ...}
message: <message is printed here>
Lua Script Exec End

this is the android output:

06-25 20:50:45.716 10313 10337 D : new Thread started.. 
06-25 20:50:55.262 10313 10337 D : size: 477 
06-25 20:50:55.262 10313 10337 D : after first while 
06-25 20:50:55.263 10313 10337 D : after readbuf alloc 
06-25 20:50:55.263 10313 10337 D : in second while 
06-25 20:50:56.263 10313 10337 D : after second delimiter
06-25 20:50:56.263 10313 10337 D : 
06-25 20:50:56.263 10313 10337 D : read buffer: { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

no message output

From what I can see, these differences may stem from your global Io. Whatever you are pulling from there, may differ on different platforms.

I’m not sure the environmental differences, but something I immediately notice is the logs are different between the two. Is the windows output running in CMD and the android running as a native application?

yep its a native library on android printing to logcat, i asked an llm the bug is in creating the reader twice, windows and android behave completely differently. here is the quote from that llm:

Root Cause of the Android Bug
The bug is caused by creating a new reader on every loop iteration.
Each connection.reader(io, buf) call starts a fresh buffered reader. 
When the first reader does a read() syscall to scan for *, the OS hands it a chunk of bytes —
on Windows that chunk happens to end right at the delimiter (separate TCP segments per send()),
but on Android all the data often arrives in one segment.
The first reader then buffers the entire payload, the second reader starts from scratch and finds nothing,
so a is null.
The fix: one reader per connection, created once, reused for all reads.

This will eagerly read as much data as is available from connection into size_buf, which might include the base64-encoced data after the first * delimiter depending on the Io implementation. So if you ended up reading e.g. 12*abcdefghijkl* from the connection, the abcdefghijkl* remaining inside size_buf will be effectively discarded, and when you create a new reader with a new buffer, and your next read will immediately read EOF.

You will probably want to redesign your implementation so that it uses only one reader for all reads.