Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add core:io test suite #4112

Merged
merged 36 commits into from
Aug 30, 2024
Merged

Conversation

Feoramund
Copy link
Contributor

This started out with me wanting io.Section_Reader to work as I expected it to: to work off of the supplied base offset. So, I wrote a test suite for core:io and found a handful of bugs with various streams and platforms.

The only thing I'm not certain of is if bytes.Buffer should have the behavior it has. It's not congruent to any other stream: its Size decreases as it reads, and when it reaches zero while reading, it clears and resets the underlying buffer. I had to add some extra handling for it in the test suite. Could use some feedback on this one. I'm inclined to say the Size shouldn't decrease and the buffer deleting itself was unexpected to me.

It's worth noting up front that I made Windows behave like POSIX with regards to read_at/write_at: these procedures will seek back to their point of origin after operation. This may not be what everyone expects, so I'm open to how this should work across the platforms. I had to fix a few platforms around that general issue with regards to 7663a20.

Note: We don't test for Haiku and I don't have an installation, so my fix for that OS is based on my fix for NetBSD's. Same case to OpenBSD.


The test suite accepts any io.Stream and runs a grab-bag of tests against whatever features it says it supports; a great benefit to having a feature query interface. Not all errors are tested for; it could be expanded, but it does a lot of sanity checking. A good first step towards more comprehensive io testing.

Right now I have tests for:

  • bytes.Reader
  • bytes.Buffer as a stream
  • io.Limited_Reader
  • io.Section_Reader
  • strings.Builder as a stream
  • os.open file descriptor streams

I think those are the basic ones, but if there's any more that need to be added, let me know.

Note2: I haven't looked into os2 much at all, save for checking which interfaces had missing query responses, so if I need to move some of the os-affected functionality into that package, I can do so.

@flysand7
Copy link
Contributor

io:core...

@flysand7
Copy link
Contributor

That said, a test suite for I/O streams sounds nice. It is an abstraction, and testing for surface-level assumptions that one might make for such streams is good, since the implementations of these streams typically aren't straightforward

@Feoramund Feoramund changed the title Add io:core test suite Add core:io test suite Aug 20, 2024
Copy link
Collaborator

@laytan laytan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love more tests 💯

core/os/file_windows.odin Show resolved Hide resolved
core/bytes/buffer.odin Outdated Show resolved Hide resolved
@Feoramund
Copy link
Contributor Author

It looks like Darwin, despite using the POSIX interface on os2 as far as I can tell, is returning no error when it should error after using a closed file descriptor. NetBSD and FreeBSD use the same interface, and they're having no issue. Mysterious.

I think the os2 interface for Windows might be causing a stack overflow somewhere, as it's consistently exiting with -1073741571 / 0xC000_00FD. I don't have the means to probe into this more deeply at the moment.

@laytan
Copy link
Collaborator

laytan commented Aug 23, 2024

Closing a file in os2 does a little more, it also frees and zeros memory. You are effectively testing a use-after-free where all bets are off. I did put in a couple print statements, and the call to size ends up calling posix.fstat(0) (because the file struct was zero'd) instead of the actual file descriptor you had before calling close. I am guessing fstat on file descriptor 0 is fine on darwin but not on the bsds, but that's just a guess.

This is not something you should test though, a use-after-free means all bets are off.

By the way, you can do -sanitize:address and it will yell at you because of the use-after-free (at least it did for me).

@Feoramund
Copy link
Contributor Author

Closing a file in os2 does a little more, it also frees and zeros memory. You are effectively testing a use-after-free where all bets are off. I did put in a couple print statements, and the call to size ends up calling posix.fstat(0) (because the file struct was zero'd) instead of the actual file descriptor you had before calling close. I am guessing fstat on file descriptor 0 is fine on darwin but not on the bsds, but that's just a guess.

This is not something you should test though, a use-after-free means all bets are off.

I see now. Since we're deleting the File_Impl itself, and that's what's stored on the io.Stream, there's no way we can safely check if it's still usable either. I wasn't aware the API wasn't designed to not handle use-after-close situations, but that's understandable now. I could add some comments around this and remove the various after-close tests. In that case, do we not need an Invalid_Stream error? I could remove that as well.

By the way, you can do -sanitize:address and it will yell at you because of the use-after-free (at least it did for me).

I know of this flag but don't use it enough, and it did indeed raise a warning when I tried it.

@laytan
Copy link
Collaborator

laytan commented Aug 23, 2024

In that case, do we not need an Invalid_Stream error?

I don't think it is needed, but it could be hit when somebody does os2.fd(file) to get the underlying OSs handle and then manually closes that through that OS API. If that just comes up as unknown I wouldn't complain, but your call.

As for not being designed to handle use after closing, I don't think that's really possible because like you said we have no idea if the pointer in the stream is still valid. IMO classifying that as a bug (basically a use-after-free) on the user's end is fine.

Even with libc, doing close(fd), if anywhere else you do something that generates a new file descriptor, and then try using the fd again, you would be doing operations on that newly created file descriptor and won't get any errors. It is just the easy case where you close and then use right away that is handled.

@Feoramund Feoramund force-pushed the fix-test-io-issues branch 3 times, most recently from c979c29 to ece3188 Compare August 26, 2024 05:07
@Feoramund
Copy link
Contributor Author

I've rebased the Invalid_Stream error out of this PR, deciding instead to document os2.close explicitly that using a file stream in any way after closing it is like a use-after-free bug, therefore no more testing post-Close cases for any io.Streams.

I had to fix a few bugs with the os2 Windows file implementation, but it's all working fine now.

@laytan
Copy link
Collaborator

laytan commented Aug 26, 2024

Looks good to me!

I just remembered that bufio also has some streams but we can also add those in the future.

@Feoramund
Copy link
Contributor Author

I just remembered that bufio also has some streams but we can also add those in the future.

I've added bufio tests now. Setting that up revealed more inconsistencies in the stream API, so now all streams should return 0, nil if they're passed an empty slice in the read/write procs.

It also looks like this has revealed an issue with EOF detection in os2 Windows. I suspect that the note in os Windows about a successful read meaning a possible EOF might be relevant, but I don't have time to investigate this at the moment.

@laytan
Copy link
Collaborator

laytan commented Aug 28, 2024

It also looks like this has revealed an issue with EOF detection in os2 Windows. I suspect that the note in os Windows about a successful read meaning a possible EOF might be relevant, but I don't have time to investigate this at the moment.

You were right about that, os2 wasn't seeing that as EOF, I have committed a fix for this and also another test failure because we were doing os2.remove on a file that wasn't closed because the bufio.Read_Writer doesn't implement close.

Really great to have these tests to make sure the streams all behave as expected! I believe this is ready to merge now?

@laytan
Copy link
Collaborator

laytan commented Aug 28, 2024

Hmm looks like ubuntu CI is deadlocking now for some reason, ran it twice and it deadlocked at the same spot 🤔

@laytan
Copy link
Collaborator

laytan commented Aug 28, 2024

Looks like a deadlock in os2/heap_linux.odin:602, only on running the tests with multiple threads, looks like a bug in that allocator.

@laytan
Copy link
Collaborator

laytan commented Aug 28, 2024

-sanitize:thread is not happy with the allocator:

WARNING: ThreadSanitizer: data race (pid=12498)
  Read of size 8 at 0x0000019553b0 by thread T1:
    #0 os2._region_retrieve_with_space-5869 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:515:8 (io+0x553368) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #1 os2.heap_alloc-5760 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:261:20 (io+0x52c93c) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #2 os2._heap_allocator_proc-5747.aligned_alloc-0 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:161:18 (io+0x581123) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #3 os2._heap_allocator_proc-5747 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:194:3 (io+0x51a940) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #4 os2.heap_allocator_proc /home/laytan/third-party/odin/core/os/os2/heap.odin:18:2 (io+0x4df03d) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #5 runtime.mem_alloc_bytes /home/laytan/third-party/odin/base/runtime/internal.odin:127:2 (io+0x501c44) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #6 runtime.new_aligned-29033 /home/laytan/third-party/odin/base/runtime/core_builtin.odin:278:2 (io+0x5319a0) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #7 runtime.new-28716 /home/laytan/third-party/odin/base/runtime/core_builtin.odin:274:2 (io+0x50068f) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #8 os2._new_file-5445 /home/laytan/third-party/odin/core/os/os2/file_linux.odin:102:2 (io+0x4ff294) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #9 os2._open-5435 /home/laytan/third-party/odin/core/os/os2/file_linux.odin:98:2 (io+0x4f5bdf) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #10 os2.open /home/laytan/third-party/odin/core/os/os2/file.odin:104:2 (io+0x5003ea) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #11 test_core_io.test_bufio_buffered_read_writer /home/laytan/third-party/odin/tests/core/io/test_core_io.odin:693:2 (io+0x55a45a) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #12 testing.run_test_task-792 /home/laytan/third-party/odin/core/testing/runner.odin:148:2 (io+0x50d2df) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #13 thread.pool_do_work /home/laytan/third-party/odin/core/thread/thread_pool.odin:312:3 (io+0x52e090) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #14 thread.pool_thread_runner-4844 /home/laytan/third-party/odin/core/thread/thread_pool.odin:62:4 (io+0x4f4a75) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #15 thread._create-4660.__unix_thread_entry_proc-0 /home/laytan/third-party/odin/core/thread/thread_unix.odin:60:4 (io+0x57e333) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)

  Previous atomic write of size 8 at 0x0000019553b0 by thread T2:
    #0 os2._new_region-5826 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:352:6 (io+0x533fc1) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #1 os2._region_retrieve_with_space-5869 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:537:2 (io+0x553592) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #2 os2.heap_alloc-5760 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:261:20 (io+0x52c93c) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #3 os2._heap_allocator_proc-5747.aligned_alloc-0 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:161:18 (io+0x581123) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #4 os2._heap_allocator_proc-5747 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:194:3 (io+0x51a940) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #5 os2.heap_allocator_proc /home/laytan/third-party/odin/core/os/os2/heap.odin:18:2 (io+0x4df03d) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #6 runtime.mem_alloc_bytes /home/laytan/third-party/odin/base/runtime/internal.odin:127:2 (io+0x501c44) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #7 runtime.new_aligned-29033 /home/laytan/third-party/odin/base/runtime/core_builtin.odin:278:2 (io+0x5319a0) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #8 runtime.new-28716 /home/laytan/third-party/odin/base/runtime/core_builtin.odin:274:2 (io+0x50068f) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #9 os2._new_file-5445 /home/laytan/third-party/odin/core/os/os2/file_linux.odin:102:2 (io+0x4ff294) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #10 os2._open-5435 /home/laytan/third-party/odin/core/os/os2/file_linux.odin:98:2 (io+0x4f5bdf) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #11 os2.open /home/laytan/third-party/odin/core/os/os2/file.odin:104:2 (io+0x5003ea) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #12 test_core_io.test_os2_file_stream /home/laytan/third-party/odin/tests/core/io/test_core_io.odin:592:2 (io+0x556cb1) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #13 testing.run_test_task-792 /home/laytan/third-party/odin/core/testing/runner.odin:148:2 (io+0x50d2df) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #14 thread.pool_do_work /home/laytan/third-party/odin/core/thread/thread_pool.odin:312:3 (io+0x52e090) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #15 thread.pool_thread_runner-4844 /home/laytan/third-party/odin/core/thread/thread_pool.odin:62:4 (io+0x4f4a75) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #16 thread._create-4660.__unix_thread_entry_proc-0 /home/laytan/third-party/odin/core/thread/thread_unix.odin:60:4 (io+0x57e333) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)

  Location is global 'os2.global_regions-5637' of size 8 at 0x0000019553b0 (io+0x19553b0)

  Thread T1 (tid=12500, running) created by main thread at:
    #0 pthread_create <null> (io+0x45d7eb) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #1 thread._create-4660 /home/laytan/third-party/odin/core/thread/thread_unix.odin:118:2 (io+0x4f06ba) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #2 thread.create /home/laytan/third-party/odin/core/thread/thread.odin:105:2 (io+0x503b2a) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #3 thread.pool_init /home/laytan/third-party/odin/core/thread/thread_pool.odin:84:3 (io+0x4f8f8f) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #4 testing.runner-817 /home/laytan/third-party/odin/core/testing/runner.odin:313:2 (io+0x51b632) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #5 main <null> (io+0x586903) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)

  Thread T2 (tid=12501, running) created by main thread at:
    #0 pthread_create <null> (io+0x45d7eb) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #1 thread._create-4660 /home/laytan/third-party/odin/core/thread/thread_unix.odin:118:2 (io+0x4f06ba) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #2 thread.create /home/laytan/third-party/odin/core/thread/thread.odin:105:2 (io+0x503b2a) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #3 thread.pool_init /home/laytan/third-party/odin/core/thread/thread_pool.odin:84:3 (io+0x4f8f8f) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #4 testing.runner-817 /home/laytan/third-party/odin/core/testing/runner.odin:313:2 (io+0x51b632) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #5 main <null> (io+0x586903) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)

SUMMARY: ThreadSanitizer: data race /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:515:8 in os2._region_retrieve_with_space-5869
==================
==================
WARNING: ThreadSanitizer: data race (pid=12498)
  Read of size 2 at 0x7ffff6bac02a by thread T1:
    #0 os2._region_retrieve_with_space-5869 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:516:3 (io+0x5533d0) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #1 os2.heap_alloc-5760 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:261:20 (io+0x52c93c) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #2 os2._heap_allocator_proc-5747.aligned_alloc-0 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:161:18 (io+0x581123) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #3 os2._heap_allocator_proc-5747 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:194:3 (io+0x51a940) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #4 os2.heap_allocator_proc /home/laytan/third-party/odin/core/os/os2/heap.odin:18:2 (io+0x4df03d) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #5 runtime.mem_alloc_bytes /home/laytan/third-party/odin/base/runtime/internal.odin:127:2 (io+0x501c44) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #6 runtime.new_aligned-29033 /home/laytan/third-party/odin/base/runtime/core_builtin.odin:278:2 (io+0x5319a0) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #7 runtime.new-28716 /home/laytan/third-party/odin/base/runtime/core_builtin.odin:274:2 (io+0x50068f) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #8 os2._new_file-5445 /home/laytan/third-party/odin/core/os/os2/file_linux.odin:102:2 (io+0x4ff294) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #9 os2._open-5435 /home/laytan/third-party/odin/core/os/os2/file_linux.odin:98:2 (io+0x4f5bdf) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #10 os2.open /home/laytan/third-party/odin/core/os/os2/file.odin:104:2 (io+0x5003ea) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #11 test_core_io.test_bufio_buffered_read_writer /home/laytan/third-party/odin/tests/core/io/test_core_io.odin:693:2 (io+0x55a45a) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #12 testing.run_test_task-792 /home/laytan/third-party/odin/core/testing/runner.odin:148:2 (io+0x50d2df) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #13 thread.pool_do_work /home/laytan/third-party/odin/core/thread/thread_pool.odin:312:3 (io+0x52e090) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #14 thread.pool_thread_runner-4844 /home/laytan/third-party/odin/core/thread/thread_pool.odin:62:4 (io+0x4f4a75) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #15 thread._create-4660.__unix_thread_entry_proc-0 /home/laytan/third-party/odin/core/thread/thread_unix.odin:60:4 (io+0x57e333) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)

  Previous write of size 2 at 0x7ffff6bac02a by thread T2:
    #0 os2.heap_alloc-5760 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:283:32 (io+0x52cd2c) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #1 os2._heap_allocator_proc-5747.aligned_alloc-0 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:161:18 (io+0x581123) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #2 os2._heap_allocator_proc-5747 /home/laytan/third-party/odin/core/os/os2/heap_linux.odin:194:3 (io+0x51a940) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #3 os2.heap_allocator_proc /home/laytan/third-party/odin/core/os/os2/heap.odin:18:2 (io+0x4df03d) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #4 runtime.mem_alloc_bytes /home/laytan/third-party/odin/base/runtime/internal.odin:127:2 (io+0x501c44) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #5 runtime.make_aligned-26168 /home/laytan/third-party/odin/base/runtime/core_builtin.odin:298:2 (io+0x535921) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #6 runtime.make_slice-25803 /home/laytan/third-party/odin/base/runtime/core_builtin.odin:312:2 (io+0x54e471) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #7 os2._read_link_cstr-5741 /home/laytan/third-party/odin/core/os/os2/file_linux.odin:325:2 (io+0x52af92) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #8 os2._get_full_path-5604 /home/laytan/third-party/odin/core/os/os2/path_linux.odin:184:19 (io+0x5163b7) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #9 os2._new_file-5445 /home/laytan/third-party/odin/core/os/os2/file_linux.odin:109:12 (io+0x4ff5e5) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #10 os2._open-5435 /home/laytan/third-party/odin/core/os/os2/file_linux.odin:98:2 (io+0x4f5bdf) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #11 os2.open /home/laytan/third-party/odin/core/os/os2/file.odin:104:2 (io+0x5003ea) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #12 test_core_io.test_os2_file_stream /home/laytan/third-party/odin/tests/core/io/test_core_io.odin:592:2 (io+0x556cb1) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #13 testing.run_test_task-792 /home/laytan/third-party/odin/core/testing/runner.odin:148:2 (io+0x50d2df) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #14 thread.pool_do_work /home/laytan/third-party/odin/core/thread/thread_pool.odin:312:3 (io+0x52e090) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #15 thread.pool_thread_runner-4844 /home/laytan/third-party/odin/core/thread/thread_pool.odin:62:4 (io+0x4f4a75) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #16 thread._create-4660.__unix_thread_entry_proc-0 /home/laytan/third-party/odin/core/thread/thread_unix.odin:60:4 (io+0x57e333) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)

  Thread T1 (tid=12500, running) created by main thread at:
    #0 pthread_create <null> (io+0x45d7eb) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #1 thread._create-4660 /home/laytan/third-party/odin/core/thread/thread_unix.odin:118:2 (io+0x4f06ba) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #2 thread.create /home/laytan/third-party/odin/core/thread/thread.odin:105:2 (io+0x503b2a) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #3 thread.pool_init /home/laytan/third-party/odin/core/thread/thread_pool.odin:84:3 (io+0x4f8f8f) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #4 testing.runner-817 /home/laytan/third-party/odin/core/testing/runner.odin:313:2 (io+0x51b632) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #5 main <null> (io+0x586903) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)

  Thread T2 (tid=12501, running) created by main thread at:
    #0 pthread_create <null> (io+0x45d7eb) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #1 thread._create-4660 /home/laytan/third-party/odin/core/thread/thread_unix.odin:118:2 (io+0x4f06ba) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #2 thread.create /home/laytan/third-party/odin/core/thread/thread.odin:105:2 (io+0x503b2a) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #3 thread.pool_init /home/laytan/third-party/odin/core/thread/thread_pool.odin:84:3 (io+0x4f8f8f) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #4 testing.runner-817 /home/laytan/third-party/odin/core/testing/runner.odin:313:2 (io+0x51b632) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)
    #5 main <null> (io+0x586903) (BuildId: ad0ef22dbbcb61f9cfde8845c7e7c7f436b36459)

This should fix seeking from `.Start`, getting the `size`, and
`read_at`.

Also make the API consistent with the other `*_init` procs in
`util.odin` by returning the `io.Reader`.
Makes the API like the other stream `init` procs.
This is consistent with the other stream `read` procs
Handles `EINVAL`, among other fixes.
These procedures must not modify the underlying file pointer.
Feoramund and others added 24 commits August 28, 2024 19:53
Platforms:
- FreeBSD
- Haiku
- Linux
- NetBSD
- OpenBSD
- Move `Seek`-related checks into OS-specific files for granularity.

Platforms:
- Darwin
- FreeBSD
- Haiku
- Linux
- NetBSD
- OpenBSD
- Relax return value requirements on errors. Only the error is checked,
  as there are multiple conflicting return styles throughout the `os`
  API.

  Some return the error along with `0`, `-1`, or another value. This can
  be smoothed out later.

- Test `os2` files now.
- No longer test streams after closing; this is considered similar to a
  use-after-free in `os2`.
Make it explicit that using a stream after closing is like a
use-after-free bug.
The previous code was jumping ahead by the specified offset, instead of
getting the current offset.
@laytan
Copy link
Collaborator

laytan commented Aug 28, 2024

I disabled the custom allocator in #4162 for now and rebased this.

@Feoramund
Copy link
Contributor Author

Feoramund commented Aug 28, 2024

I had a glance over the changes, and it seems alright. I have nothing more to add, and I'd say we're ready to merge after a review.

EDIT: Though, I don't think I ever got any feedback on bytes.Buffer's odd behavior. If someone wanted to address that, that would be helpful.

@laytan
Copy link
Collaborator

laytan commented Aug 28, 2024

The only thing I'm not certain of is if bytes.Buffer should have the behavior it has. It's not congruent to any other stream: its Size decreases as it reads, and when it reaches zero while reading, it clears and resets the underlying buffer. I had to add some extra handling for it in the test suite. Could use some feedback on this one. I'm inclined to say the Size shouldn't decrease and the buffer deleting itself was unexpected to me.

EDIT: Though, I don't think I ever got any feedback on bytes.Buffer's odd behavior. If someone wanted to address that, that would be helpful.

I do not know why it is that way myself.

The behavior makes "some" sense that it returns the size that is left to be read, destroying itself is a little weird to me too.

@Feoramund
Copy link
Contributor Author

I thought on this for a day. I think any major API change to bytes.Buffer would be out of scope of this PR, though I've considered that this sort of functionality could be split out into a different type of buffer or stream procedure, if there's a need for it.

The decrementing size and buffer self-destruct is something that needs to be explicitly documented at least, since this is the oddball of the streams. It's also worth noting that bytes.Buffer is the only stream I could find that makes an entire copy of the buffer slice you give it in its _init proc, instead of directly using the slice (which makes sense given that it uses a dynamic array under the hood), but that too can be surprising in its own way (especially for very large slices under certain memory constraints).

It's possible there could be two different buffers for bytes, one dynamic and the other a slice, then two (or more?) stream procedures that could be selected when converting each buffer to a stream which either limits or expands its functionality depending on whether the user wants a buffer that decreases in size or self-destructs. Thoughts for another day, at least.

@gingerBill gingerBill merged commit b020b91 into odin-lang:master Aug 30, 2024
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants