xref: /reactos/sdk/lib/ucrt/stdio/stream.cpp (revision e3e520d1)
1 //
2 // getstream.cpp
3 //
4 //      Copyright (c) Microsoft Corporation.  All rights reserved.
5 //
6 // Defines _getstream(), which finds and locks a stream that is available for use.
7 //
8 #include <corecrt_internal_stdio.h>
9 
10 
11 
12 static __crt_stdio_stream __cdecl find_or_allocate_unused_stream_nolock() throw()
13 {
14     __crt_stdio_stream_data** const first_stream = __piob + _IOB_ENTRIES;
15     __crt_stdio_stream_data** const last_stream  = first_stream + _nstream - _IOB_ENTRIES;
16 
17     for (__crt_stdio_stream_data** it = first_stream; it != last_stream; ++it)
18     {
19         // First, check to see whether the stream is valid and free for use:
20         {
21             __crt_stdio_stream stream(*it);
22             if (stream.valid())
23             {
24                 if (stream.is_in_use())
25                     continue;
26 
27                 stream.lock();
28                 if (!stream.try_allocate())
29                 {
30                     stream.unlock();
31                     continue;
32                 }
33 
34                 return stream;
35             }
36         }
37 
38         // Otherwise, there is no stream at this index yet, so we allocate one
39         // and return it:
40         {
41             *it = _calloc_crt_t(__crt_stdio_stream_data, 1).detach();
42             if (*it == nullptr)
43                 break;
44 
45             // Initialize the stream.  Everything requires zero-initialization
46             // (which we get from calloc), except the file handle and the lock:
47             (*it)->_file = -1;
48             __acrt_InitializeCriticalSectionEx(&(*it)->_lock, _CORECRT_SPINCOUNT, 0);
49 
50             __crt_stdio_stream stream(*it);
51 
52             // Note:  This attempt will always succeed, because we hold the only
53             // pointer to the stream object (since we just allocated it):
54             stream.try_allocate();
55             stream.lock();
56 
57             return stream;
58         }
59     }
60 
61     return __crt_stdio_stream();
62 }
63 
64 
65 
66 // Finds a stream not in use and makes it available to the caller.  It is
67 // intended for internal use inside the library only.  It returns a pointer to
68 // a free stream, or nullptr if all are in use.  A stream becomes allocated
69 // only if the caller decides to use it by setting a mode (r, w, or r/w).  The
70 // stream is returned locked; the caller is responsible for unlocking the stream.
71 __crt_stdio_stream __cdecl __acrt_stdio_allocate_stream() throw()
72 {
73     __crt_stdio_stream stream;
74 
75     __acrt_lock(__acrt_stdio_index_lock);
76     __try
77     {
78         stream = find_or_allocate_unused_stream_nolock();
79         if (!stream.valid())
80             __leave;
81 
82         stream->_cnt = 0;
83         stream->_tmpfname = nullptr;
84         stream->_ptr = nullptr;
85         stream->_base = nullptr;
86         stream->_file = -1;
87     }
88     __finally
89     {
90         __acrt_unlock(__acrt_stdio_index_lock);
91     }
92     __endtry
93 
94     return stream;
95 }
96 
97 void __cdecl __acrt_stdio_free_stream(__crt_stdio_stream stream) throw()
98 {
99     stream->_ptr      = nullptr;
100     stream->_base     = nullptr;
101     stream->_cnt      =  0;
102     stream->_file     = -1;
103     stream->_charbuf  =  0;
104     stream->_bufsiz   =  0;
105     stream->_tmpfname = nullptr;
106     stream.deallocate();
107 }
108