History

Basic implementation of Sys module functions - Sys.create, Sys.open, Sys.read, Sys.write - Sys.tokenize

Code analysis violations Bug in scheduler - race during with worker thread shutdown.

arr committed 10 years ago

Commit 992a33f1 parent 6d77c2d18 files · +695 −243Browse files

Showing the changes to src/vm/sys/Sysmod.cpp (1 of 18 files). Show all

Unified · Split

src/vm/sys/Sysmod.cpp+375−97
@@ -5,6 +5,7 @@ · 4 unmodified lines
5//5//
6 6
7#include <array>7#include <array>
8#include <algorithm>
8#include <chrono>9#include <chrono>
9#include <thread>10#include <thread>
10#include <iostream>11#include <iostream>
@@ -13,6 +14,7 @@ · 2 unmodified lines
13#include <exceptions.h>14#include <exceptions.h>
14#include <debug.h>15#include <debug.h>
15#include <utf8.h>16#include <utf8.h>
17#include <vm_memory.h>
16#include "Sysmod.h"18#include "Sysmod.h"
17#include "sys_utils.h"19#include "sys_utils.h"
18 20
@@ -69,99 +71,153 @@ namespace · 50 unmodified lines
69 };71 };
70 72
71 const word_t Sysmodlen = 43;73 const word_t Sysmodlen = 43;
72}
73 74
74std::shared_ptr<const type_descriptor_t> T_Qid;75 std::shared_ptr<const type_descriptor_t> T_Qid;
75std::shared_ptr<const type_descriptor_t> T_Dir;76 std::shared_ptr<const type_descriptor_t> T_Dir;
76std::shared_ptr<const type_descriptor_t> T_FD;77 std::shared_ptr<const type_descriptor_t> T_FD;
77std::shared_ptr<const type_descriptor_t> T_Connection;78 std::shared_ptr<const type_descriptor_t> T_Connection;
78std::shared_ptr<const type_descriptor_t> T_FileIO;79 std::shared_ptr<const type_descriptor_t> T_FileIO;
79 80
80struct vm_fd_t final : public Sys_FD81 class vm_fd_t final : public Sys_FD
81{
82 static void finalizer(vm_alloc_t *fileHandle)
83 {82 {
84 auto fd = fileHandle->get_allocation<vm_fd_t>();83 public: // static
85 fd->~vm_fd_t();84 static void finalizer(vm_alloc_t *fileHandle)
86 }85 {
86 auto fd = fileHandle->get_allocation<vm_fd_t>();
87
88 if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>())
89 {
90 debug::log_msg(
91 debug::component_trace_t::builtin,
92 debug::log_level_t::debug,
93 "sys: fd: finalize: %d\n",
94 fd->fd);
95 }
96
97 sys::drop_fd_record(fd->fd);
98 fd->~vm_fd_t();
99 }
87 100
88 vm_fd_t(word_t fd, std::unique_ptr<std::iostream> s)101 static std::tuple<word_t, vm_alloc_t *> create(std::unique_ptr<std::iostream> s, word_t fd_mode, vm_string_t *fd_path)
89 : Sys_FD{ fd }102 {
90 , _istream{ s.get() }103 assert(s != nullptr);
91 , _ostream{ s.get() }104 auto alloc = vm_alloc_t::allocate(T_FD);
92 {105 const auto fd = sys::create_fd_record(alloc);
93 assert(s != nullptr);106 auto vm_fd = ::new(alloc->get_allocation())vm_fd_t{ fd, std::move(s), fd_mode, fd_path };
94 _stream = std::move(s);
95 }
96 107
97 vm_fd_t(word_t fd, std::unique_ptr<std::istream> s)108 return std::make_tuple(vm_fd->fd, alloc);
98 : Sys_FD{ fd }109 }
99 , _istream{ s.get() }
100 , _ostream{ nullptr }
101 {
102 assert(s != nullptr);
103 _stream = std::move(s);
104 }
105 110
106 vm_fd_t(word_t fd, std::unique_ptr<std::ostream> s)111 private:
107 : Sys_FD{ fd }112 vm_fd_t(const word_t fd, std::unique_ptr<std::iostream> s, const word_t fd_mode, vm_string_t *fd_path)
108 , _istream{ nullptr }113 : Sys_FD{ fd }
109 , _ostream{ s.get() }114 , _fd_mode{ fd_mode }
110 {115 , _fd_path{ fd_path }
111 assert(s != nullptr);116 , _istream{ s.get() }
112 _stream = std::move(s);117 , _ostream{ s.get() }
113 }118 {
119 _stream = std::move(s);
114 120
115 vm_fd_t(word_t fd, std::istream &s)121 if (_fd_path != nullptr)
116 : Sys_FD{ fd }122 _fd_path->add_ref();
117 , _istream{ &s }123 }
118 , _ostream{ nullptr }
119 {
120 }
121 124
122 vm_fd_t(word_t fd, std::ostream &s)125 public:
123 : Sys_FD{ fd }126 vm_fd_t(const word_t fd, std::istream &is)
124 , _istream{ nullptr }127 : Sys_FD{ fd }
125 , _ostream{ &s }128 , _fd_mode{}
126 {129 , _fd_path{ nullptr }
127 }130 , _istream{ &is }
131 , _ostream{ nullptr }
132 {
133 }
128 134
129 ~vm_fd_t()135 vm_fd_t(const word_t fd, std::ostream &os)
130 {136 : Sys_FD{ fd }
131 debug::assign_debug_pointer(&_istream);137 , _fd_mode{}
132 debug::assign_debug_pointer(&_ostream);138 , _fd_path{ nullptr }
139 , _istream{ nullptr }
140 , _ostream{ &os }
141 {
142 }
133 143
134 if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())144 ~vm_fd_t()
135 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: file descriptor: %d", fd);145 {
136 }146 debug::assign_debug_pointer(&_istream);
147 debug::assign_debug_pointer(&_ostream);
148 if (_stream != nullptr)
149 _stream.reset();
137 150
138 std::istream& input() const151 // Honor the close flag
139 {152 if ((_fd_mode & Sys_ORCLOSE) && _fd_path != nullptr)
140 if (_istream == nullptr)153 std::remove(_fd_path->str());
141 throw vm_user_exception{ "File descriptor not open for write" };
142 154
143 return *_istream;155 dec_ref_count_and_free(_fd_path);
144 }156 debug::assign_debug_pointer(&_fd_path);
157 }
158
159 word_t get_original_mode() const
160 {
161 return _fd_mode;
162 }
163
164 vm_string_t *get_original_path() const
165 {
166 return _fd_path;
167 }
168
169 word_t read(const word_t buffer_size_in_bytes, void *buffer)
170 {
171 if (_istream == nullptr)
172 throw vm_system_exception{ "File descriptor not open for read operation" };
173
174 _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
175 if (_istream->bad())
176 throw vm_user_exception{ "Read operation error" };
145 177
146 std::ostream& output() const178 return static_cast<word_t>(_istream->gcount());
179 }
180
181 void write(const word_t buffer_size_in_bytes, void *buffer)
182 {
183 if (_ostream == nullptr)
184 throw vm_system_exception{ "File descriptor not open for write operation" };
185
186 _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
187 if (_ostream->bad())
188 throw vm_user_exception{ "Write operation error" };
189 }
190
191 private:
192 const word_t _fd_mode;
193 vm_string_t *_fd_path;
194 std::istream *_istream;
195 std::ostream *_ostream;
196 std::unique_ptr<std::ios> _stream;
197 };
198
199 vm_fd_t *fd_stdin = nullptr;
200 vm_fd_t *fd_stdout = nullptr;
201 vm_fd_t *fd_stderr = nullptr;
202
203 std::ios::openmode convert_to_openmode(const word_t mode)
147 {204 {
148 if (_ostream == nullptr)205 auto om = std::ios::openmode{};
149 throw vm_user_exception{ "File descriptor not open for read" };
150 206
151 return *_ostream;207 // Lowest bit is for read/write.
152 }208 if (mode & Sys_OWRITE)
209 om |= std::ios::out;
210 else
211 om |= std::ios::in; // Sys_OREAD
153 212
154private:213 if (mode & Sys_ORDWR)
155 std::istream *_istream;214 om |= (std::ios::in | std::ios::out);
156 std::ostream *_ostream;
157 std::unique_ptr<std::ios> _stream;
158};
159 215
160namespace216 if (mode & Sys_OTRUNC)
161{217 om |= std::ios::trunc;
162 vm_alloc_t *vm_stdin = nullptr;218
163 vm_alloc_t *vm_stdout = nullptr;219 return om;
164 vm_alloc_t *vm_stderr = nullptr;220 }
165}221}
166 222
167void223void
@@ -176,14 +232,26 @@ Sysmodinit(void) · 8 unmodified lines
176 232
177 // Initialize default file descriptors233 // Initialize default file descriptors
178 234
179 vm_stdin = vm_alloc_t::allocate(T_FD);235 {
180 ::new(vm_stdin->get_allocation()) vm_fd_t{ 0, std::cin };236 auto vm_stdin = vm_alloc_t::allocate(T_FD);
237 const auto fd = sys::create_fd_record(vm_stdin);
238 fd_stdin = ::new(vm_stdin->get_allocation())vm_fd_t{ fd, std::cin };
239 debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stdin: %d\n", fd);
240 }
181 241
182 vm_stdout = vm_alloc_t::allocate(T_FD);242 {
183 ::new(vm_stdout->get_allocation()) vm_fd_t{ 1, std::cout };243 auto vm_stdout = vm_alloc_t::allocate(T_FD);
244 const auto fd = sys::create_fd_record(vm_stdout);
245 fd_stdout = ::new(vm_stdout->get_allocation())vm_fd_t{ fd, std::cout };
246 debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stdout: %d\n", fd);
247 }
184 248
185 vm_stderr = vm_alloc_t::allocate(T_FD);249 {
186 ::new(vm_stderr->get_allocation()) vm_fd_t{ 2, std::cerr };250 auto vm_stderr = vm_alloc_t::allocate(T_FD);
251 const auto fd = sys::create_fd_record(vm_stderr);
252 fd_stderr = ::new(vm_stderr->get_allocation())vm_fd_t{ fd, std::cerr };
253 debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stderr: %d\n", fd);
254 }
187}255}
188 256
189void257void
@@ -260,19 +328,19 @@ Sys_char2byte(vm_registers_t &r, vm_t &vm) · 70 unmodified lines
260 328
261 const auto rune = static_cast<rune_t>(fp.c);329 const auto rune = static_cast<rune_t>(fp.c);
262 330
263 static_assert(Sys_UTFmax == sizeof(rune_t), "UTF max should be same size as rune");331 static_assert(Sys_UTFmax == sizeof(rune), "UTF max should be same size as rune");
264 uint8_t buffer_local[Sys_UTFmax];332 uint8_t buffer_local[Sys_UTFmax];
265 auto begin = buffer_local;333 auto begin = buffer_local;
266 334
267 auto end = utf8::encode(rune, begin);335 auto end = utf8::encode(rune, begin);
268 const auto count = static_cast<word_t>(end - begin);336 const auto count = static_cast<word_t>(end - begin);
269 337
270 // Initialize for failure.
271 *fp.ret = 0;
272
273 const auto final_index = n + count;338 const auto final_index = n + count;
274 if (final_index > buffer->get_length())339 if (final_index > buffer->get_length())
340 {
341 *fp.ret = 0;
275 return;342 return;
343 }
276 344
277 // Insert the bytes into the buffer345 // Insert the bytes into the buffer
278 for (auto i = word_t{ n }; i < final_index; ++i)346 for (auto i = word_t{ n }; i < final_index; ++i)
@@ -295,7 +363,41 @@ void · 16 unmodified lines
295Sys_create(vm_registers_t &r, vm_t &vm)363Sys_create(vm_registers_t &r, vm_t &vm)
296{364{
297 auto &fp = r.stack.peek_frame()->base<F_Sys_create>();365 auto &fp = r.stack.peek_frame()->base<F_Sys_create>();
298 throw vm_system_exception{ "Instruction not implemented" };366
367 auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
368 const auto open_mode = convert_to_openmode(fp.mode);
369
370 auto cfs_flag = sys::cfs_flags_t::none;
371 if (fp.mode & Sys_OEXCL)
372 cfs_flag = (sys::cfs_flags_t::atomic | sys::cfs_flags_t::ensure_create);
373
374 // [TODO] Apply the requested permissions to the file.
375 auto file_stream = sys::create_file_stream(path->str(), (open_mode | std::ios::binary), cfs_flag);
376
377 // Return null if file stream is not open.
378 if (file_stream == nullptr)
379 {
380 *fp.ret = nullptr;
381 return;
382 }
383
384 vm_alloc_t *fd_alloc;
385 auto fd = word_t{};
386 std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path);
387
388 if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>())
389 {
390 debug::log_msg(
391 debug::component_trace_t::builtin,
392 debug::log_level_t::debug,
393 "sys: create: %d >>%s<< %#x %#x\n",
394 fd,
395 path->str(),
396 fp.mode,
397 fp.perm);
398 }
399
400 *fp.ret = fd_alloc->get_allocation();
299}401}
300 402
301void403void
@@ -417,7 +519,36 @@ void · 115 unmodified lines
417Sys_open(vm_registers_t &r, vm_t &vm)519Sys_open(vm_registers_t &r, vm_t &vm)
418{520{
419 auto &fp = r.stack.peek_frame()->base<F_Sys_open>();521 auto &fp = r.stack.peek_frame()->base<F_Sys_open>();
420 throw vm_system_exception{ "Instruction not implemented" };522
523 auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
524 const auto open_mode = convert_to_openmode(fp.mode);
525
526 // [TODO] Handle opening devices - not just files (i.e. /dev/random, /dev/source, /proc/1234)
527 auto file_stream = sys::create_file_stream(path->str(), (open_mode | std::ios::binary));
528
529 // Return null if file stream is not open.
530 if (file_stream == nullptr)
531 {
532 *fp.ret = nullptr;
533 return;
534 }
535
536 vm_alloc_t *fd_alloc;
537 auto fd = word_t{};
538 std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path);
539
540 if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>())
541 {
542 debug::log_msg(
543 debug::component_trace_t::builtin,
544 debug::log_level_t::debug,
545 "sys: open: %d >>%s<< %#x\n",
546 fd,
547 path->str(),
548 fp.mode);
549 }
550
551 *fp.ret = fd_alloc->get_allocation();
421}552}
422 553
423void554void
@@ -453,16 +584,16 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 29 unmodified lines
453 584
454 auto static_buffer = std::array<char, 1024>{};585 auto static_buffer = std::array<char, 1024>{};
455 auto msg_args = &fp.vargs;586 auto msg_args = &fp.vargs;
456 auto written = printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data());587 auto written = sys::printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data());
457 if (written >= 0)588 if (written >= 0)
458 {589 {
459 vm_stdout->get_allocation<vm_fd_t>()->output() << static_buffer.data();590 fd_stdout->write(written, static_buffer.data());
460 }591 }
461 else592 else
462 {593 {
463 auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2);594 auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2);
464 written = printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer);595 written = sys::printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer);
465 vm_stdout->get_allocation<vm_fd_t>()->output() << static_buffer.data();596 fd_stdout->write(written, dynamic_buffer.data());
466 }597 }
467 598
468 *fp.ret = written;599 *fp.ret = written;
@@ -479,7 +610,26 @@ void · 10 unmodified lines
479Sys_read(vm_registers_t &r, vm_t &vm)610Sys_read(vm_registers_t &r, vm_t &vm)
480{611{
481 auto &fp = r.stack.peek_frame()->base<F_Sys_read>();612 auto &fp = r.stack.peek_frame()->base<F_Sys_read>();
482 throw vm_system_exception{ "Instruction not implemented" };613
614 const auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.buf);
615 assert(buffer->get_element_type()->size_in_bytes == intrinsic_type_desc::type<byte_t>()->size_in_bytes);
616
617 auto n = fp.n;
618 if (buffer == nullptr || n <= 0)
619 {
620 *fp.ret = 0;
621 return;
622 }
623
624 // [SPEC] Supplying a size greater than the buffer length is
625 // equivalent to indicating the entire buffer should be filled.
626 n = std::min(n, buffer->get_length());
627
628 auto fd_alloc = vm_alloc_t::from_allocation(fp.fd);
629 assert(fd_alloc->alloc_type == T_FD);
630
631 auto fd = fd_alloc->get_allocation<vm_fd_t>();
632 *fp.ret = fd->read(n, buffer->at(0));
483}633}
484 634
485void635void
@@ -511,9 +661,7 @@ Sys_sleep(vm_registers_t &r, vm_t &vm) · 25 unmodified lines
511 const auto period_ms = fp.period;661 const auto period_ms = fp.period;
512 662
513 // [TODO] Thread should yield to another according to the sleep() specification.663 // [TODO] Thread should yield to another according to the sleep() specification.
514 r.current_thread_state = vm_thread_state_t::release;
515 std::this_thread::sleep_for(std::chrono::milliseconds(period_ms));664 std::this_thread::sleep_for(std::chrono::milliseconds(period_ms));
516 r.current_thread_state = vm_thread_state_t::ready;
517 665
518 *fp.ret = 0;666 *fp.ret = 0;
519}667}
@@ -539,11 +687,120 @@ Sys_stream(vm_registers_t &r, vm_t &vm) · 19 unmodified lines
539 throw vm_system_exception{ "Instruction not implemented" };687 throw vm_system_exception{ "Instruction not implemented" };
540}688}
541 689
690namespace
691{
692 bool is_delim(const rune_t c, const vm_string_t &delim)
693 {
694 const auto max = delim.get_length();
695 assert(max > 0);
696
697 // Optimization for 3 or less delimiters
698 switch (max)
699 {
700 default:
701 break;
702 case 1:
703 return (delim.get_rune(0) == c);
704 case 2:
705 return (delim.get_rune(0) == c) || (delim.get_rune(1) == c);
706 case 3:
707 return (delim.get_rune(0) == c) || (delim.get_rune(1) == c) || (delim.get_rune(2) == c);
708 }
709
710 for (auto i = word_t{ 0 }; i < max; ++i)
711 {
712 if (delim.get_rune(i) == c)
713 return true;
714 }
715
716 return false;
717 }
718}
719
542void720void
543Sys_tokenize(vm_registers_t &r, vm_t &vm)721Sys_tokenize(vm_registers_t &r, vm_t &vm)
544{722{
545 auto &fp = r.stack.peek_frame()->base<F_Sys_tokenize>();723 auto &fp = r.stack.peek_frame()->base<F_Sys_tokenize>();
546 throw vm_system_exception{ "Instruction not implemented" };724
725 // Initialize for failure.
726 fp.ret->t0 = 0;
727 dec_ref_count_and_free(vm_alloc_t::from_allocation(fp.ret->t1));
728 fp.ret->t1 = nullptr;
729
730 auto s = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
731 if (s == nullptr || s->get_length() == 0)
732 return;
733
734 auto curr_node = new vm_list_t{ intrinsic_type_desc::type<pointer_t>() };
735
736 // Set the head of the list
737 fp.ret->t1 = curr_node->get_allocation();
738
739 auto delim = vm_alloc_t::from_allocation<vm_string_t>(fp.delim);
740 if (delim == nullptr || delim->get_length() == 0)
741 {
742 // Add supplied string to list
743 pt_ref(curr_node->value()) = s->get_allocation();
744 s->add_ref();
745
746 fp.ret->t0 = 1;
747 return;
748 }
749
750 // Tokenize the string
751 vm_list_t *prev_node = nullptr;
752 auto n = word_t{ 0 };
753 auto begin = word_t{ 0 };
754 auto end = word_t{ 0 };
755 const auto len = s->get_length();
756 while (begin < len)
757 {
758 // Consume delimiters
759 while (begin < len)
760 {
761 const auto rune = s->get_rune(begin);
762 if (!is_delim(rune, *delim))
763 break;
764 ++begin;
765 }
766
767 end = begin;
768
769 // Consume non-delimiters
770 while (end < len)
771 {
772 const auto rune = s->get_rune(end);
773 if (is_delim(rune, *delim))
774 break;
775 ++end;
776 }
777
778 // No progress - It is and isn't a delimiter (i.e. no more characters).
779 if (begin == end)
780 break;
781
782 // Current node is initially non-null
783 if (curr_node == nullptr)
784 curr_node = new vm_list_t{ intrinsic_type_desc::type<pointer_t>() };
785
786 const auto token = new vm_string_t{ *s, begin, end };
787 pt_ref(curr_node->value()) = token->get_allocation();
788
789 // Previous node is initially null
790 if (prev_node != nullptr)
791 {
792 prev_node->set_tail(curr_node);
793 curr_node->release();
794 }
795
796 prev_node = curr_node;
797 curr_node = nullptr;
798
799 begin = end;
800 ++n;
801 }
802
803 fp.ret->t0 = n;
547}804}
548 805
549void806void
@@ -571,7 +828,28 @@ void · 21 unmodified lines
571Sys_write(vm_registers_t &r, vm_t &vm)828Sys_write(vm_registers_t &r, vm_t &vm)
572{829{
573 auto &fp = r.stack.peek_frame()->base<F_Sys_write>();830 auto &fp = r.stack.peek_frame()->base<F_Sys_write>();
574 throw vm_system_exception{ "Instruction not implemented" };831
832 const auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.buf);
833 assert(buffer->get_element_type()->size_in_bytes == intrinsic_type_desc::type<byte_t>()->size_in_bytes);
834
835 auto n = fp.n;
836 if (buffer == nullptr || n <= 0)
837 {
838 *fp.ret = 0;
839 return;
840 }
841
842 // [SPEC] Supplying a size greater than the buffer length is
843 // equivalent to indicating the entire buffer should be written.
844 n = std::min(n, buffer->get_length());
845
846 auto fd_alloc = vm_alloc_t::from_allocation(fp.fd);
847 assert(fd_alloc->alloc_type == T_FD);
848
849 auto fd = fd_alloc->get_allocation<vm_fd_t>();
850 fd->write(n, buffer->at(0));
851
852 *fp.ret = n;
575}853}
576 854
577void855void