diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-06-11 10:57:03 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-06-11 10:57:03 +0100 |
commit | 868fa2fe829687343ffae624259930155e16dbd8 (patch) | |
tree | 54d316199dd9739c57c3aacd131853bbd6554a94 /native_client_sdk | |
parent | bb1bdbd796f966b5bf11f40ecbea12621c7bfac9 (diff) | |
download | chromium_org-868fa2fe829687343ffae624259930155e16dbd8.tar.gz |
Merge from Chromium at DEPS revision r205460
This commit was generated by merge_to_master.py.
Change-Id: I4a744a5e426bd3bb378d887cfa56fe054742a540
Diffstat (limited to 'native_client_sdk')
114 files changed, 5080 insertions, 3882 deletions
diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py index 5b793536a7..9839dff510 100755 --- a/native_client_sdk/src/build_tools/build_sdk.py +++ b/native_client_sdk/src/build_tools/build_sdk.py @@ -54,7 +54,9 @@ import oshelpers CYGTAR = os.path.join(NACL_DIR, 'build', 'cygtar.py') NACLPORTS_URL = 'https://naclports.googlecode.com/svn/trunk/src' -NACLPORTS_REV = 757 +NACLPORTS_REV = 774 + +GYPBUILD_DIR = 'gypbuild' options = None @@ -434,21 +436,30 @@ TOOLCHAIN_LIBS = { def GypNinjaInstall(pepperdir, platform, toolchains): - build_dir = 'gypbuild' + build_dir = GYPBUILD_DIR ninja_out_dir = os.path.join(OUT_DIR, build_dir, 'Release') tools_files = [ - ['dump_syms', 'dump_syms'], ['sel_ldr', 'sel_ldr_x86_32'], - ['ncval_x86_32', 'ncval_x86_32'], - ['ncval_arm', 'ncval_arm'], + ['ncval_new', 'ncval'], ['irt_core_newlib_x32.nexe', 'irt_core_x86_32.nexe'], ['irt_core_newlib_x64.nexe', 'irt_core_x86_64.nexe'], ] + if sys.platform not in ['cygwin', 'win32']: + minidump_files = [ + ['dump_syms', 'dump_syms'], + ['minidump_dump', 'minidump_dump'], + ['minidump_stackwalk', 'minidump_stackwalk'] + ] + tools_files.extend(minidump_files) + + # TODO(binji): dump_syms doesn't currently build on Windows. See + # http://crbug.com/245456 + if platform != 'win': + tools_files.append(['dump_syms', 'dump_syms']) if platform != 'mac': # Mac doesn't build 64-bit binaries. tools_files.append(['sel_ldr64', 'sel_ldr_x86_64']) - tools_files.append(['ncval_x86_64', 'ncval_x86_64']) if platform == 'linux': tools_files.append(['nacl_helper_bootstrap', @@ -473,10 +484,10 @@ def GypNinjaInstall(pepperdir, platform, toolchains): tc_dir = 'tc_' + tc lib_dir = 'lib' + archname if archname == 'arm': - build_dir = 'gypbuild-arm' + build_dir = GYPBUILD_DIR + '-arm' tcdir = '%s_arm_%s' % (platform, tc) else: - build_dir = 'gypbuild' + build_dir = GYPBUILD_DIR tcdir = '%s_x86_%s' % (platform, tc) ninja_out_dir = os.path.join(OUT_DIR, build_dir, 'Release') @@ -502,22 +513,18 @@ def GypNinjaBuild_NaCl(platform, rel_out_dir): out_dir_arm = MakeNinjaRelPath(rel_out_dir + '-arm') GypNinjaBuild('ia32', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir) GypNinjaBuild('arm', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir_arm) - GypNinjaBuild('ia32', gyp_py, all_gyp, 'ncval_x86_32', out_dir) - GypNinjaBuild(None, gyp_py, all_gyp, 'ncval_arm', out_dir) + GypNinjaBuild('ia32', gyp_py, all_gyp, 'ncval_new', out_dir) if platform == 'win': NinjaBuild('sel_ldr64', out_dir) - NinjaBuild('ncval_x86_64', out_dir) elif platform == 'linux': out_dir_64 = MakeNinjaRelPath(rel_out_dir + '-64') GypNinjaBuild('x64', gyp_py, nacl_core_sdk_gyp, 'sel_ldr', out_dir_64) - GypNinjaBuild('x64', gyp_py, all_gyp, 'ncval_x86_64', out_dir_64) - # We only need sel_ldr and ncval_x86_64 from the 64-bit out directory. + # We only need sel_ldr from the 64-bit out directory. # sel_ldr needs to be renamed, so we'll call it sel_ldr64. files_to_copy = [ ('sel_ldr', 'sel_ldr64'), - ('ncval_x86_64', 'ncval_x86_64'), ('nacl_helper_bootstrap', 'nacl_helper_bootstrap64'), ] @@ -528,10 +535,16 @@ def GypNinjaBuild_NaCl(platform, rel_out_dir): def GypNinjaBuild_Breakpad(platform, rel_out_dir): + # TODO(binji): dump_syms doesn't currently build on Windows. See + # http://crbug.com/245456 + if platform == 'win': + return + gyp_py = os.path.join(SRC_DIR, 'build', 'gyp_chromium') out_dir = MakeNinjaRelPath(rel_out_dir) gyp_file = os.path.join(SRC_DIR, 'breakpad', 'breakpad.gyp') - GypNinjaBuild('ia32', gyp_py, gyp_file, 'dump_syms', out_dir) + build_list = ['dump_syms', 'minidump_dump', 'minidump_stackwalk'] + GypNinjaBuild('ia32', gyp_py, gyp_file, build_list, out_dir) def GypNinjaBuild_PPAPI(arch, rel_out_dir): @@ -554,6 +567,16 @@ def GypNinjaBuild_Pnacl(rel_out_dir, target_arch): targets = ['pnacl_irt_shim'] GypNinjaBuild(target_arch, gyp_py, gyp_file, targets, out_dir, False) + gyp_py = os.path.join(NACL_DIR, 'build', 'gyp_nacl') + gyp_file = os.path.join(NACL_DIR, 'src', 'untrusted', 'minidump_generator', + 'minidump_generator.gyp') + targets = ['minidump_generator_lib'] + GypNinjaBuild(target_arch, gyp_py, gyp_file, targets, out_dir, False) + + gyp_file = os.path.join(NACL_DIR, 'src', 'untrusted', 'nacl', 'nacl.gyp') + targets = ['nacl_exception_lib'] + GypNinjaBuild(target_arch, gyp_py, gyp_file, targets, out_dir, False) + def GypNinjaBuild(arch, gyp_py_script, gyp_file, targets, out_dir, force_arm_gcc=True): @@ -593,8 +616,8 @@ def NinjaBuild(targets, out_dir): def BuildStepBuildToolchains(pepperdir, platform, toolchains): buildbot_common.BuildStep('SDK Items') - GypNinjaBuild_NaCl(platform, 'gypbuild') - GypNinjaBuild_Breakpad(platform, 'gypbuild') + GypNinjaBuild_NaCl(platform, GYPBUILD_DIR) + GypNinjaBuild_Breakpad(platform, GYPBUILD_DIR) tcname = platform + '_x86' newlibdir = os.path.join(pepperdir, 'toolchain', tcname + '_newlib') @@ -602,10 +625,10 @@ def BuildStepBuildToolchains(pepperdir, platform, toolchains): pnacldir = os.path.join(pepperdir, 'toolchain', tcname + '_pnacl') if set(toolchains) & set(['glibc', 'newlib']): - GypNinjaBuild_PPAPI('ia32', 'gypbuild') + GypNinjaBuild_PPAPI('ia32', GYPBUILD_DIR) if 'arm' in toolchains: - GypNinjaBuild_PPAPI('arm', 'gypbuild-arm') + GypNinjaBuild_PPAPI('arm', GYPBUILD_DIR + '-arm') GypNinjaInstall(pepperdir, platform, toolchains) @@ -624,6 +647,8 @@ def BuildStepBuildToolchains(pepperdir, platform, toolchains): 'arm') if 'pnacl' in toolchains: + # shell=True is needed on windows to enable searching of the PATH: + # http://bugs.python.org/issue8557 shell = platform == 'win' buildbot_common.Run( GetSconsArgs(pnacldir, pepperdir, 'x86', '32'), @@ -632,7 +657,7 @@ def BuildStepBuildToolchains(pepperdir, platform, toolchains): for arch in ('ia32', 'arm'): # Fill in the latest native pnacl shim library from the chrome build. - build_dir = 'gypbuild-pnacl-' + arch + build_dir = GYPBUILD_DIR + '-pnacl-' + arch GypNinjaBuild_Pnacl(build_dir, arch) pnacl_libdir_map = {'ia32': 'x86-64', 'arm': 'arm'} release_build_dir = os.path.join(OUT_DIR, build_dir, 'Release', @@ -643,6 +668,16 @@ def BuildStepBuildToolchains(pepperdir, platform, toolchains): os.path.join(release_build_dir, 'libpnacl_irt_shim.a'), GetPNaClNativeLib(pnacldir, pnacl_libdir_map[arch])) + release_build_dir = os.path.join(OUT_DIR, build_dir, 'Release', + 'gen', 'tc_pnacl_newlib', 'lib') + buildbot_common.CopyFile( + os.path.join(release_build_dir, 'libminidump_generator.a'), + GetPNaClNativeLib(pnacldir, pnacl_libdir_map[arch])) + + buildbot_common.CopyFile( + os.path.join(release_build_dir, 'libnacl_exception.a'), + GetPNaClNativeLib(pnacldir, pnacl_libdir_map[arch])) + InstallNaClHeaders(GetToolchainNaClInclude('pnacl', pnacldir, 'x86'), 'newlib') @@ -741,9 +776,30 @@ def GenerateNotice(fileroot, output_filename='NOTICE', extra_files=None): def BuildStepVerifyFilelist(pepperdir, platform): buildbot_common.BuildStep('Verify SDK Files') - verify_filelist.Verify(platform, os.path.join(SCRIPT_DIR, 'sdk_files.list'), - pepperdir) - print 'OK' + file_list_path = os.path.join(SCRIPT_DIR, 'sdk_files.list') + try: + verify_filelist.Verify(platform, file_list_path, pepperdir) + print 'OK' + except verify_filelist.ParseException, e: + buildbot_common.ErrorExit('Parsing sdk_files.list failed:\n\n%s' % e) + except verify_filelist.VerifyException, e: + file_list_rel = os.path.relpath(file_list_path) + verify_filelist_py = os.path.splitext(verify_filelist.__file__)[0] + '.py' + verify_filelist_py = os.path.relpath(verify_filelist_py) + pepperdir_rel = os.path.relpath(pepperdir) + + msg = """\ +SDK verification failed: + +%s +Add/remove files from %s to fix. + +Run: + ./%s %s %s +to test.""" % (e, file_list_rel, verify_filelist_py, file_list_rel, + pepperdir_rel) + buildbot_common.ErrorExit(msg) + def BuildStepTarBundle(pepper_ver, tarfile): @@ -959,7 +1015,7 @@ def main(args): # Archive on non-trybots. if options.archive: BuildStepArchiveBundle('build', pepper_ver, clnumber, tarfile) - if platform == 'linux': + if options.build_ports and platform == 'linux': BuildStepArchiveBundle('naclports', pepper_ver, clnumber, ports_tarfile) BuildStepArchiveSDKTools() diff --git a/native_client_sdk/src/build_tools/buildbot_common.py b/native_client_sdk/src/build_tools/buildbot_common.py index 4de37f8d83..157cd0b856 100644 --- a/native_client_sdk/src/build_tools/buildbot_common.py +++ b/native_client_sdk/src/build_tools/buildbot_common.py @@ -14,6 +14,7 @@ from build_paths import SDK_SRC_DIR, NACL_DIR sys.path.append(os.path.join(SDK_SRC_DIR, 'tools')) import oshelpers +import getos def IsSDKBuilder(): """Returns True if this script is running on an SDK builder. @@ -39,7 +40,7 @@ def IsSDKTrybot(): def ErrorExit(msg): """Write and error to stderr, then exit with 1 signaling failure.""" - sys.stderr.write(msg + '\n') + sys.stderr.write(str(msg) + '\n') sys.exit(1) @@ -101,16 +102,19 @@ def Run(args, cwd=None, env=None, shell=False): so this should be avoided when not using platform dependent shell scripts.""" # We need to modify the environment to build host on Windows. - if not env: - if sys.platform.startswith('cygwin') or sys.platform.startswith('win'): - env = GetWindowsEnvironment() - else: - env = os.environ + if not env and getos.GetPlatform() == 'win': + env = GetWindowsEnvironment() print 'Running: ' + ' '.join(args) sys.stdout.flush() sys.stderr.flush() - subprocess.check_call(args, cwd=cwd, env=env, shell=shell) + try: + subprocess.check_call(args, cwd=cwd, env=env, shell=shell) + except subprocess.CalledProcessError as e: + sys.stdout.flush() + sys.stderr.flush() + ErrorExit('buildbot_common: %s' % e) + sys.stdout.flush() sys.stderr.flush() @@ -159,12 +163,17 @@ def RemoveFile(dst): BOT_GSUTIL = '/b/build/scripts/slave/gsutil' +# On Windows, the current working directory may be on a different drive than +# gsutil. +WIN_BOT_GSUTIL = 'E:' + BOT_GSUTIL LOCAL_GSUTIL = 'gsutil' def GetGsutil(): if os.environ.get('BUILDBOT_BUILDERNAME') \ and not os.environ.get('BUILDBOT_FAKE'): + if getos.GetPlatform() == 'win': + return WIN_BOT_GSUTIL return BOT_GSUTIL else: return LOCAL_GSUTIL @@ -174,10 +183,14 @@ def Archive(filename, bucket_path, cwd=None, step_link=True): """Upload the given filename to Google Store.""" full_dst = 'gs://%s/%s' % (bucket_path, filename) - subprocess.check_call( - '%s cp -a public-read %s %s' % (GetGsutil(), filename, full_dst), - shell=True, - cwd=cwd) + # Since GetGsutil() might just return 'gsutil' and expect it to be looked + # up in the PATH, we must pass shell=True on windows. + # Without shell=True the windows implementation of subprocess.call will not + # search the PATH for the executable: http://bugs.python.org/issue8557 + shell = getos.GetPlatform() == 'win' + + cmd = [GetGsutil(), 'cp', '-a', 'public-read', filename, full_dst] + Run(cmd, shell=shell, cwd=cwd) url = 'https://commondatastorage.googleapis.com/'\ '%s/%s' % (bucket_path, filename) if step_link: diff --git a/native_client_sdk/src/build_tools/buildbot_run.py b/native_client_sdk/src/build_tools/buildbot_run.py index f5e4ba585d..c74308e724 100755 --- a/native_client_sdk/src/build_tools/buildbot_run.py +++ b/native_client_sdk/src/build_tools/buildbot_run.py @@ -14,9 +14,12 @@ run. import buildbot_common import os +import subprocess import sys + from buildbot_common import Run -from build_paths import SDK_SRC_DIR, SCRIPT_DIR +from build_paths import SRC_DIR, SDK_SRC_DIR, SCRIPT_DIR +import getos def StepRunUnittests(): @@ -33,7 +36,25 @@ def StepRunUnittests(): def StepBuildSDK(args): - Run([sys.executable, 'build_sdk.py'] + args, cwd=SCRIPT_DIR) + is_win = getos.GetPlatform() == 'win' + + # Windows has a path length limit of 255 characters, after joining cwd with a + # relative path. Use subst before building to keep the path lengths short. + if is_win: + subst_drive = 'S:' + root_dir = os.path.dirname(SRC_DIR) + new_root_dir = subst_drive + '\\' + subprocess.check_call(['subst', subst_drive, root_dir]) + new_script_dir = os.path.join(new_root_dir, + os.path.relpath(SCRIPT_DIR, root_dir)) + else: + new_script_dir = SCRIPT_DIR + + try: + Run([sys.executable, 'build_sdk.py'] + args, cwd=new_script_dir) + finally: + if is_win: + subprocess.check_call(['subst', '/D', subst_drive]) def StepTestSDK(): diff --git a/native_client_sdk/src/build_tools/generate_make.py b/native_client_sdk/src/build_tools/generate_make.py index 2c87f7c306..faad723a43 100644 --- a/native_client_sdk/src/build_tools/generate_make.py +++ b/native_client_sdk/src/build_tools/generate_make.py @@ -160,6 +160,9 @@ def FindAndCopyFiles(src_files, root, search_dirs, dst_dir): Trace('Skipping "%s", destination "%s" is newer.' % ( src_file, dst_file)) continue + dst_path = os.path.dirname(dst_file) + if not os.path.exists(dst_path): + buildbot_common.MakeDir(dst_path) buildbot_common.CopyFile(src_file, dst_file) diff --git a/native_client_sdk/src/build_tools/sdk_files.list b/native_client_sdk/src/build_tools/sdk_files.list index b1144262b9..67eafd07e2 100644 --- a/native_client_sdk/src/build_tools/sdk_files.list +++ b/native_client_sdk/src/build_tools/sdk_files.list @@ -114,6 +114,14 @@ examples/demo/drive/Makefile examples/demo/drive/manifest.json [win]examples/demo/make.bat examples/demo/Makefile +[win]examples/demo/life/make.bat +examples/demo/life/Makefile +examples/demo/life/background.js +examples/demo/life/common.js +examples/demo/life/icon128.png +examples/demo/life/index.html +examples/demo/life/life.c +examples/demo/life/manifest.json examples/demo/nacl_io/background.js examples/demo/nacl_io/common.js examples/demo/nacl_io/example.js @@ -137,25 +145,34 @@ examples/demo/pi_generator/index.html examples/demo/pi_generator/Makefile examples/demo/pi_generator/manifest.json examples/demo/pi_generator/pi_generator.cc +examples/demo/voronoi/background.js +examples/demo/voronoi/common.js +examples/demo/voronoi/example.js +examples/demo/voronoi/icon128.png +examples/demo/voronoi/index.html +examples/demo/voronoi/Makefile +[win]examples/demo/voronoi/make.bat +examples/demo/voronoi/manifest.json +examples/demo/voronoi/voronoi.cc examples/favicon.ico +[win]examples/getting_started/hello_world/make.bat +examples/getting_started/hello_world/Makefile +examples/getting_started/hello_world/manifest.json examples/getting_started/hello_world/background.js examples/getting_started/hello_world/common.js examples/getting_started/hello_world/example.js examples/getting_started/hello_world/hello_world.c examples/getting_started/hello_world/icon128.png examples/getting_started/hello_world/index.html -[win]examples/getting_started/hello_world/make.bat -examples/getting_started/hello_world/Makefile -examples/getting_started/hello_world/manifest.json -examples/getting_started/hello_world_ppapi_main/background.js -examples/getting_started/hello_world_ppapi_main/common.js -examples/getting_started/hello_world_ppapi_main/example.js -examples/getting_started/hello_world_ppapi_main/hello_world.c -examples/getting_started/hello_world_ppapi_main/icon128.png -examples/getting_started/hello_world_ppapi_main/index.html -[win]examples/getting_started/hello_world_ppapi_main/make.bat -examples/getting_started/hello_world_ppapi_main/Makefile -examples/getting_started/hello_world_ppapi_main/manifest.json +[win]examples/getting_started/simple_hello_world/make.bat +examples/getting_started/simple_hello_world/Makefile +examples/getting_started/simple_hello_world/manifest.json +examples/getting_started/simple_hello_world/background.js +examples/getting_started/simple_hello_world/common.js +examples/getting_started/simple_hello_world/example.js +examples/getting_started/simple_hello_world/hello_world.c +examples/getting_started/simple_hello_world/icon128.png +examples/getting_started/simple_hello_world/index.html [win]examples/getting_started/make.bat examples/getting_started/Makefile examples/httpd.cmd @@ -215,6 +232,7 @@ include/json/reader.h include/json/value.h include/json/writer.h include/KHR/khrplatform.h +include/nacl_io/error.h include/nacl_io/inode_pool.h include/nacl_io/kernel_handle.h include/nacl_io/kernel_intercept.h @@ -230,6 +248,7 @@ include/nacl_io/mount_mem.h include/nacl_io/mount_node_dir.h include/nacl_io/mount_node.h include/nacl_io/mount_node_html5fs.h +include/nacl_io/mount_node_http.h include/nacl_io/mount_node_mem.h include/nacl_io/mount_passthrough.h include/nacl_io/nacl_io.h @@ -259,6 +278,7 @@ include/ppapi/c/dev/ppb_graphics_2d_dev.h include/ppapi/c/dev/ppb_ime_input_event_dev.h include/ppapi/c/dev/ppb_keyboard_input_event_dev.h include/ppapi/c/dev/ppb_memory_dev.h +include/ppapi/c/dev/ppb_net_address_dev.h include/ppapi/c/dev/ppb_opengles2ext_dev.h include/ppapi/c/dev/ppb_printing_dev.h include/ppapi/c/dev/ppb_resource_array_dev.h @@ -359,6 +379,7 @@ include/ppapi/cpp/dev/font_dev.h include/ppapi/cpp/dev/graphics_2d_dev.h include/ppapi/cpp/dev/ime_input_event_dev.h include/ppapi/cpp/dev/memory_dev.h +include/ppapi/cpp/dev/net_address_dev.h include/ppapi/cpp/dev/printing_dev.h include/ppapi/cpp/dev/resource_array_dev.h include/ppapi/cpp/dev/scriptable_object_deprecated.h @@ -443,12 +464,10 @@ include/ppapi/cpp/view.h include/ppapi/cpp/websocket.h include/ppapi/gles2/gl2ext_ppapi.h include/ppapi/lib/gl/gles2/gl2ext_ppapi.h -include/ppapi_main/ppapi_event.h -include/ppapi_main/ppapi_instance2d.h -include/ppapi_main/ppapi_instance3d.h -include/ppapi_main/ppapi_instance.h -include/ppapi_main/ppapi_main.h -include/ppapi_main/ppapi_queue.h +include/ppapi_simple/ps.h +include/ppapi_simple/ps_event.h +include/ppapi_simple/ps_instance.h +include/ppapi_simple/ps_main.h include/ppapi/utility/completion_callback_factory.h include/ppapi/utility/completion_callback_factory_thread_traits.h include/ppapi/utility/graphics/paint_aggregator.h @@ -463,9 +482,11 @@ include/ppapi/utility/websocket/websocket_api.h [win]include/win/pthread.h [win]include/win/sched.h [win]include/win/semaphore.h -include/utils/auto_lock.h -include/utils/macros.h -include/utils/ref_object.h +include/sdk_util/auto_lock.h +include/sdk_util/macros.h +include/sdk_util/ref_object.h +include/sdk_util/thread_safe_queue.h +include/sdk_util/thread_pool.h lib/glibc_x86_32/Debug/libjsoncpp.a lib/glibc_x86_32/Debug/libjsoncpp.so lib/glibc_x86_32/Debug/libnacl_io.a @@ -476,7 +497,10 @@ lib/glibc_x86_32/Debug/libppapi_cpp_private.a lib/glibc_x86_32/Debug/libppapi_cpp_private.so lib/glibc_x86_32/Debug/libppapi_gles2.a lib/glibc_x86_32/Debug/libppapi_gles2.so -lib/glibc_x86_32/Debug/libppapi_main.a +lib/glibc_x86_32/Debug/libppapi_simple.a +lib/glibc_x86_32/Debug/libppapi_simple.so +lib/glibc_x86_32/Debug/libsdk_util.a +lib/glibc_x86_32/Debug/libsdk_util.so lib/glibc_x86_32/Release/libjsoncpp.a lib/glibc_x86_32/Release/libjsoncpp.so lib/glibc_x86_32/Release/libnacl_io.a @@ -487,7 +511,10 @@ lib/glibc_x86_32/Release/libppapi_cpp_private.a lib/glibc_x86_32/Release/libppapi_cpp_private.so lib/glibc_x86_32/Release/libppapi_gles2.a lib/glibc_x86_32/Release/libppapi_gles2.so -lib/glibc_x86_32/Release/libppapi_main.a +lib/glibc_x86_32/Release/libppapi_simple.a +lib/glibc_x86_32/Release/libppapi_simple.so +lib/glibc_x86_32/Release/libsdk_util.a +lib/glibc_x86_32/Release/libsdk_util.so lib/glibc_x86_64/Debug/libjsoncpp.a lib/glibc_x86_64/Debug/libjsoncpp.so lib/glibc_x86_64/Debug/libnacl_io.a @@ -498,7 +525,10 @@ lib/glibc_x86_64/Debug/libppapi_cpp_private.a lib/glibc_x86_64/Debug/libppapi_cpp_private.so lib/glibc_x86_64/Debug/libppapi_gles2.a lib/glibc_x86_64/Debug/libppapi_gles2.so -lib/glibc_x86_64/Debug/libppapi_main.a +lib/glibc_x86_64/Debug/libppapi_simple.a +lib/glibc_x86_64/Debug/libppapi_simple.so +lib/glibc_x86_64/Debug/libsdk_util.a +lib/glibc_x86_64/Debug/libsdk_util.so lib/glibc_x86_64/Release/libjsoncpp.a lib/glibc_x86_64/Release/libjsoncpp.so lib/glibc_x86_64/Release/libnacl_io.a @@ -509,7 +539,10 @@ lib/glibc_x86_64/Release/libppapi_cpp_private.a lib/glibc_x86_64/Release/libppapi_cpp_private.so lib/glibc_x86_64/Release/libppapi_gles2.a lib/glibc_x86_64/Release/libppapi_gles2.so -lib/glibc_x86_64/Release/libppapi_main.a +lib/glibc_x86_64/Release/libppapi_simple.a +lib/glibc_x86_64/Release/libppapi_simple.so +lib/glibc_x86_64/Release/libsdk_util.a +lib/glibc_x86_64/Release/libsdk_util.so [linux]lib/${PLATFORM}_host/Debug/libjsoncpp.a [linux]lib/${PLATFORM}_host/Debug/libppapi.a [linux]lib/${PLATFORM}_host/Debug/libppapi_cpp.a @@ -534,60 +567,70 @@ lib/glibc_x86_64/Release/libppapi_main.a [win]lib/${PLATFORM}_x86_32_host/Release/ppapi_gles2.lib [win]lib/${PLATFORM}_x86_32_host/Release/ppapi.lib [win]lib/${PLATFORM}_x86_32_host/Release/pthread.lib +[win]lib/${PLATFORM}_x86_32_host/Debug/sdk_util.lib +[win]lib/${PLATFORM}_x86_32_host/Release/sdk_util.lib lib/newlib_arm/Debug/liberror_handling.a lib/newlib_arm/Debug/libjsoncpp.a lib/newlib_arm/Debug/libnacl_io.a lib/newlib_arm/Debug/libppapi_cpp.a lib/newlib_arm/Debug/libppapi_cpp_private.a lib/newlib_arm/Debug/libppapi_gles2.a -lib/newlib_arm/Debug/libppapi_main.a +lib/newlib_arm/Debug/libppapi_simple.a +lib/newlib_arm/Debug/libsdk_util.a lib/newlib_arm/Release/liberror_handling.a lib/newlib_arm/Release/libjsoncpp.a lib/newlib_arm/Release/libnacl_io.a lib/newlib_arm/Release/libppapi_cpp.a lib/newlib_arm/Release/libppapi_cpp_private.a lib/newlib_arm/Release/libppapi_gles2.a -lib/newlib_arm/Release/libppapi_main.a +lib/newlib_arm/Release/libppapi_simple.a +lib/newlib_arm/Release/libsdk_util.a lib/newlib_x86_32/Debug/liberror_handling.a lib/newlib_x86_32/Debug/libjsoncpp.a lib/newlib_x86_32/Debug/libnacl_io.a lib/newlib_x86_32/Debug/libppapi_cpp.a lib/newlib_x86_32/Debug/libppapi_cpp_private.a lib/newlib_x86_32/Debug/libppapi_gles2.a -lib/newlib_x86_32/Debug/libppapi_main.a +lib/newlib_x86_32/Debug/libppapi_simple.a +lib/newlib_x86_32/Debug/libsdk_util.a lib/newlib_x86_32/Release/liberror_handling.a lib/newlib_x86_32/Release/libjsoncpp.a lib/newlib_x86_32/Release/libnacl_io.a lib/newlib_x86_32/Release/libppapi_cpp.a lib/newlib_x86_32/Release/libppapi_cpp_private.a lib/newlib_x86_32/Release/libppapi_gles2.a -lib/newlib_x86_32/Release/libppapi_main.a +lib/newlib_x86_32/Release/libppapi_simple.a +lib/newlib_x86_32/Release/libsdk_util.a lib/newlib_x86_64/Debug/liberror_handling.a lib/newlib_x86_64/Debug/libjsoncpp.a lib/newlib_x86_64/Debug/libnacl_io.a lib/newlib_x86_64/Debug/libppapi_cpp.a lib/newlib_x86_64/Debug/libppapi_cpp_private.a lib/newlib_x86_64/Debug/libppapi_gles2.a -lib/newlib_x86_64/Debug/libppapi_main.a +lib/newlib_x86_64/Debug/libppapi_simple.a +lib/newlib_x86_64/Debug/libsdk_util.a lib/newlib_x86_64/Release/liberror_handling.a lib/newlib_x86_64/Release/libjsoncpp.a lib/newlib_x86_64/Release/libnacl_io.a lib/newlib_x86_64/Release/libppapi_cpp.a lib/newlib_x86_64/Release/libppapi_cpp_private.a lib/newlib_x86_64/Release/libppapi_gles2.a -lib/newlib_x86_64/Release/libppapi_main.a +lib/newlib_x86_64/Release/libppapi_simple.a +lib/newlib_x86_64/Release/libsdk_util.a lib/pnacl/Debug/libjsoncpp.a lib/pnacl/Debug/libnacl_io.a lib/pnacl/Debug/libppapi_cpp.a lib/pnacl/Debug/libppapi_cpp_private.a lib/pnacl/Debug/libppapi_gles2.a -lib/pnacl/Debug/libppapi_main.a +lib/pnacl/Debug/libppapi_simple.a +lib/pnacl/Debug/libsdk_util.a lib/pnacl/Release/libjsoncpp.a lib/pnacl/Release/libnacl_io.a lib/pnacl/Release/libppapi_cpp.a lib/pnacl/Release/libppapi_cpp_private.a lib/pnacl/Release/libppapi_gles2.a -lib/pnacl/Release/libppapi_main.a +lib/pnacl/Release/libppapi_simple.a +lib/pnacl/Release/libsdk_util.a LICENSE NOTICE README @@ -626,6 +669,7 @@ src/nacl_io/mount_mem.cc src/nacl_io/mount_node.cc src/nacl_io/mount_node_dir.cc src/nacl_io/mount_node_html5fs.cc +src/nacl_io/mount_node_http.cc src/nacl_io/mount_node_mem.cc src/nacl_io/mount_passthrough.cc src/nacl_io/nacl_io.cc @@ -636,6 +680,7 @@ src/ppapi_cpp/array_output.cc src/ppapi_cpp/audio.cc src/ppapi_cpp/audio_config.cc src/ppapi_cpp/core.cc +src/ppapi_cpp/directory_entry.cc src/ppapi_cpp/file_io.cc src/ppapi_cpp/file_ref.cc src/ppapi_cpp/file_system.cc @@ -672,17 +717,17 @@ src/ppapi_gles2/gl2ext_ppapi.c src/ppapi_gles2/gles2.c [win]src/ppapi_gles2/make.bat src/ppapi_gles2/Makefile -[win]src/ppapi_main/make.bat -src/ppapi_main/Makefile -src/ppapi_main/ppapi_instance2d.cc -src/ppapi_main/ppapi_instance3d.cc -src/ppapi_main/ppapi_instance.cc -src/ppapi_main/ppapi_main.cc -src/ppapi_main/ppapi_queue.cc +[win]src/ppapi_simple/make.bat +src/ppapi_simple/Makefile +src/ppapi_simple/ps.cc +src/ppapi_simple/ps_event.cc +src/ppapi_simple/ps_instance.cc +src/ppapi_simple/ps_main.cc src/ppapi_cpp_private/Makefile [win]src/ppapi_cpp_private/make.bat src/ppapi_cpp_private/file_io_private.cc src/ppapi_cpp_private/net_address_private.cc +src/ppapi_cpp_private/pass_file_handle.cc src/ppapi_cpp_private/tcp_socket_private.cc src/ppapi_cpp_private/udp_socket_private.cc src/ppapi_cpp_private/ext_crx_file_system_private.cc @@ -836,6 +881,9 @@ src/ppapi_cpp_private/x509_certificate_private.cc [win]src/ppapi/make.bat [win,linux]src/ppapi/Makefile [win,linux]src/ppapi/ppapi_externs.c +src/sdk_util/Makefile +[win]src/sdk_util/make.bat +src/sdk_util/thread_pool.cc toolchain/${PLATFORM}_arm_newlib/arm-nacl/include/irt.h toolchain/${PLATFORM}_arm_newlib/arm-nacl/include/irt_ppapi.h toolchain/${PLATFORM}_arm_newlib/arm-nacl/include/nacl/dynamic_annotations.h @@ -944,7 +992,8 @@ tools/compiler-wrapper.py tools/create_nmf.py tools/decode_dump.py [linux,mac]tools/dump_syms -[win]tools/dump_syms.exe +[linux,mac]tools/minidump_stackwalk +[linux,mac]tools/minidump_dump tools/genhttpfs.py tools/getos.py tools/host_gcc.mk @@ -957,17 +1006,10 @@ tools/nacl_gcc.mk [linux]tools/nacl_helper_bootstrap_x86_32 [linux]tools/nacl_helper_bootstrap_x86_64 tools/nacl_llvm.mk -[linux,mac]tools/ncval_arm -[win]tools/ncval_arm.exe -[linux,mac]tools/ncval_x86_32 -[linux]tools/ncval_x86_64 -[win]tools/ncval_x86_32.exe -[win]tools/ncval_x86_64.exe +tools/ncval${EXE_EXT} tools/oshelpers.py tools/oshelpers.pyc tools/quote.py tools/run.py -[linux,mac]tools/sel_ldr_x86_32 -[linux]tools/sel_ldr_x86_64 -[win]tools/sel_ldr_x86_32.exe -[win]tools/sel_ldr_x86_64.exe +tools/sel_ldr_x86_32${EXE_EXT} +[linux,win]tools/sel_ldr_x86_64${EXE_EXT} diff --git a/native_client_sdk/src/build_tools/sdk_tools/command/info.py b/native_client_sdk/src/build_tools/sdk_tools/command/info.py index c412ddd281..6bf310fc6a 100644 --- a/native_client_sdk/src/build_tools/sdk_tools/command/info.py +++ b/native_client_sdk/src/build_tools/sdk_tools/command/info.py @@ -23,13 +23,11 @@ def Info(manifest, bundle_names): for key in sorted(bundle.iterkeys()): value = bundle[key] if key == manifest_util.ARCHIVES_KEY: - archive = bundle.GetHostOSArchive() - print ' Archive:' - if archive: - for archive_key in sorted(archive.iterkeys()): - print ' %s: %s' % (archive_key, archive[archive_key]) - else: - print ' No archives for this host.' + for archive in bundle.GetArchives(): + print ' Archive:' + if archive: + for archive_key in sorted(archive.iterkeys()): + print ' %s: %s' % (archive_key, archive[archive_key]) elif key not in (manifest_util.ARCHIVES_KEY, manifest_util.NAME_KEY): print ' %s: %s' % (key, value) print diff --git a/native_client_sdk/src/build_tools/tests/sdktools_commands_test.py b/native_client_sdk/src/build_tools/tests/sdktools_commands_test.py index 2fae323c97..8abef29bfe 100755 --- a/native_client_sdk/src/build_tools/tests/sdktools_commands_test.py +++ b/native_client_sdk/src/build_tools/tests/sdktools_commands_test.py @@ -99,6 +99,18 @@ class TestCommands(SdkToolsTestCase): self.assertTrue('pepper_24' in output) self.assertFalse(re.search(r'[uU]nknown', output)) + def testInfoMultipleArchives(self): + """The info command should display multiple archives.""" + bundle = self._AddDummyBundle(self.manifest, 'pepper_26') + archive2 = self._MakeDummyArchive('pepper_26', tarname='pepper_26_more', + filename='dummy2.txt') + archive2.host_os = 'all' + bundle.AddArchive(archive2) + self._WriteManifest() + output = self._Run(['info', 'pepper_26']) + self.assertTrue('pepper_26' in output) + self.assertTrue('pepper_26_more' in output) + def testListBasic(self): """The list command should display basic information about remote bundles.""" diff --git a/native_client_sdk/src/build_tools/verify_filelist.py b/native_client_sdk/src/build_tools/verify_filelist.py index 7b75e91488..b97e4205ec 100755 --- a/native_client_sdk/src/build_tools/verify_filelist.py +++ b/native_client_sdk/src/build_tools/verify_filelist.py @@ -37,6 +37,7 @@ class Rules(object): self.exact_filenames = set() self.filename = filename self.platform = platform + self.exe_ext = '.exe' if platform == 'win' else '' if platform not in VALID_PLATFORMS: raise ParseException(self.filename, 1, 'Unknown platform %s' % platform) @@ -67,6 +68,7 @@ class Rules(object): pattern = match.group(2) pattern = pattern.replace('${PLATFORM}', self.platform) + pattern = pattern.replace('${EXE_EXT}', self.exe_ext) if '*' in pattern: # glob pattern diff --git a/native_client_sdk/src/examples/api/file_io/example.js b/native_client_sdk/src/examples/api/file_io/example.js index e73f4b9462..1183c9cd43 100644 --- a/native_client_sdk/src/examples/api/file_io/example.js +++ b/native_client_sdk/src/examples/api/file_io/example.js @@ -19,6 +19,7 @@ function attachListeners() { document.getElementById('saveButton').addEventListener('click', saveFile); document.getElementById('loadButton').addEventListener('click', loadFile); document.getElementById('deleteButton').addEventListener('click', deleteFile); + document.getElementById('listButton').addEventListener('click', listDir); } function loadFile() { @@ -55,6 +56,17 @@ function deleteFile() { } } +function listDir() { + if (common.naclModule) { + var dirName = document.getElementById('dirName').value; + + // Package a message using a simple protocol containing: + // instruction file_name_length file_name + var msg = "ls " + dirName.length + " " + dirName; + common.naclModule.postMessage(msg); + } +} + // Called by the common.js module. function handleMessage(message_event) { var messageParts = message_event.data.split("|", 3); diff --git a/native_client_sdk/src/examples/api/file_io/file_io.cc b/native_client_sdk/src/examples/api/file_io/file_io.cc index 9de72f8f6a..d8e27afd0f 100644 --- a/native_client_sdk/src/examples/api/file_io/file_io.cc +++ b/native_client_sdk/src/examples/api/file_io/file_io.cc @@ -11,6 +11,7 @@ #include "ppapi/c/pp_stdint.h" #include "ppapi/c/ppb_file_io.h" +#include "ppapi/cpp/directory_entry.h" #include "ppapi/cpp/file_io.h" #include "ppapi/cpp/file_ref.h" #include "ppapi/cpp/file_system.h" @@ -39,6 +40,7 @@ namespace { const char* const kLoadPrefix = "ld"; const char* const kSavePrefix = "sv"; const char* const kDeletePrefix = "de"; +const char* const kListPrefix = "ls"; } /// The Instance class. One of these exists for each instance of your NaCl @@ -133,6 +135,13 @@ class FileIoInstance : public pp::Instance { callback_factory_.NewCallback(&FileIoInstance::Delete, file_name)); return; } + + if (instruction.compare(kListPrefix) == 0) { + const std::string& dir_name = file_name; + file_thread_.message_loop().PostWork( + callback_factory_.NewCallback(&FileIoInstance::List, dir_name)); + return; + } } void OpenFileSystem(int32_t /* result */) { @@ -267,6 +276,35 @@ class FileIoInstance : public pp::Instance { ShowStatusMessage("File deleted"); } + void List(int32_t /* result */, const std::string& dir_name) { + if (!file_system_ready_) { + ShowErrorMessage("File system is not open", PP_ERROR_FAILED); + return; + } + pp::FileRef ref(file_system_, dir_name.c_str()); + + // Pass ref along to keep it alive. + ref.ReadDirectoryEntries(callback_factory_.NewCallbackWithOutput( + &FileIoInstance::ListCallback, ref)); + } + + void ListCallback(int32_t result, + const std::vector<pp::DirectoryEntry>& entries, + pp::FileRef /* unused_ref */) { + if (result != PP_OK) { + ShowErrorMessage("List failed", result); + return; + } + + std::string buffer = "File list:"; + for (size_t i = 0; i < entries.size(); ++i) { + pp::Var name = entries[i].file_ref().GetName(); + if (name.is_string()) + buffer += " " + name.AsString(); + } + ShowStatusMessage(buffer); + } + /// Encapsulates our simple javascript communication protocol void ShowErrorMessage(const std::string& message, int32_t result) { std::stringstream ss; diff --git a/native_client_sdk/src/examples/api/file_io/index.html b/native_client_sdk/src/examples/api/file_io/index.html index 7625bd69c0..7c62ebdd9c 100644 --- a/native_client_sdk/src/examples/api/file_io/index.html +++ b/native_client_sdk/src/examples/api/file_io/index.html @@ -29,6 +29,10 @@ <button id="loadButton" action="">Load</button> <button id="deleteButton" action="">Delete</button> + <br>Directory Name + <input type="text" id="dirName" action="" value="/"> + <button id="listButton" action="">List</button> + <!-- The NaCl plugin will be embedded inside the element with id "listener". See common.js.--> <div id="listener"></div> diff --git a/native_client_sdk/src/examples/demo/life/example.dsc b/native_client_sdk/src/examples/demo/life/example.dsc new file mode 100644 index 0000000000..215cfa8e8b --- /dev/null +++ b/native_client_sdk/src/examples/demo/life/example.dsc @@ -0,0 +1,18 @@ +{ + 'TOOLS': ['newlib', 'glibc', 'pnacl'], + 'TARGETS': [ + { + 'NAME' : 'life', + 'TYPE' : 'main', + 'SOURCES' : [ + 'life.c', + ], + 'DEPS': ['ppapi_simple', 'nacl_io'], + 'LIBS': ['ppapi_cpp', 'ppapi', 'pthread'] + } + ], + 'DEST': 'examples/demo', + 'NAME': 'life', + 'TITLE': "Conway's Life", + 'GROUP': 'Demo' +} diff --git a/native_client_sdk/src/examples/hello_world_instance3d/index.html b/native_client_sdk/src/examples/demo/life/index.html index fa132dc7fd..626f532b5b 100644 --- a/native_client_sdk/src/examples/hello_world_instance3d/index.html +++ b/native_client_sdk/src/examples/demo/life/index.html @@ -11,17 +11,10 @@ found in the LICENSE file. <title>{{title}}</title> <script type="text/javascript" src="common.js"></script> </head> -<body data-width="640" data-height="480" {{attrs}}> +<body data-width="640" data-height="640" {{attrs}}> <h1>{{title}}</h1> <h2>Status: <code id="statusField">NO-STATUS</code></h2> <p> - The Hello World instance3d example is similar to the Hello World GLES 2.0 - example, but uses the ppapi_main and nacl_io libraries to simplify the code: - <ul> - <li>URL loading is handled by a synchronous read from an HTTP mount.</li> - <li>Instance creation is hidden; instead the functions ppapi_main and - PPAPIRender are used.</li> - </ul> </p> <!-- The NaCl plugin will be embedded inside the element with id "listener". See common.js.--> diff --git a/native_client_sdk/src/examples/demo/life/life.c b/native_client_sdk/src/examples/demo/life/life.c new file mode 100644 index 0000000000..389d10e1fb --- /dev/null +++ b/native_client_sdk/src/examples/demo/life/life.c @@ -0,0 +1,305 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/ppb_core.h" +#include "ppapi/c/ppb_fullscreen.h" +#include "ppapi/c/ppb_graphics_2d.h" +#include "ppapi/c/ppb_image_data.h" +#include "ppapi/c/ppb_input_event.h" +#include "ppapi/c/ppb_instance.h" +#include "ppapi/c/ppb_view.h" + +#include "ppapi_simple/ps_event.h" +#include "ppapi_simple/ps_main.h" + +PPB_Core* g_pCore; +PPB_Fullscreen* g_pFullscreen; +PPB_Graphics2D* g_pGraphics2D; +PPB_ImageData* g_pImageData; +PPB_Instance* g_pInstance; +PPB_View* g_pView; +PPB_InputEvent* g_pInputEvent; +PPB_KeyboardInputEvent* g_pKeyboardInput; +PPB_MouseInputEvent* g_pMouseInput; +PPB_TouchInputEvent* g_pTouchInput; + +struct { + PP_Resource ctx; + struct PP_Size size; + int bound; + uint8_t* cell_in; + uint8_t* cell_out; +} g_Context; + + +const unsigned int kInitialRandSeed = 0xC0DE533D; + +#define MakeRGBA(r, g, b, a) \ + (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) + + +/* + * Given a count of cells in a 3x3 grid where cells are worth 1 except for + * the center which is worth 9, this is a color representation of how + * "alive" that cell is making for a more interesting representation than + * a binary alive or dead. + */ +const uint32_t kNeighborColors[] = { + MakeRGBA(0x00, 0x00, 0x00, 0xff), + MakeRGBA(0x00, 0x40, 0x00, 0xff), + MakeRGBA(0x00, 0x60, 0x00, 0xff), + MakeRGBA(0x00, 0x80, 0x00, 0xff), + MakeRGBA(0x00, 0xA0, 0x00, 0xff), + MakeRGBA(0x00, 0xC0, 0x00, 0xff), + MakeRGBA(0x00, 0xE0, 0x00, 0xff), + MakeRGBA(0x00, 0x00, 0x00, 0xff), + MakeRGBA(0x00, 0x40, 0x00, 0xff), + MakeRGBA(0x00, 0x60, 0x00, 0xff), + MakeRGBA(0x00, 0x80, 0x00, 0xff), + MakeRGBA(0x00, 0xA0, 0x00, 0xff), + MakeRGBA(0x00, 0xC0, 0x00, 0xff), + MakeRGBA(0x00, 0xE0, 0x00, 0xff), + MakeRGBA(0x00, 0xFF, 0x00, 0xff), + MakeRGBA(0x00, 0xFF, 0x00, 0xff), + MakeRGBA(0x00, 0xFF, 0x00, 0xff), + MakeRGBA(0x00, 0xFF, 0x00, 0xff), +}; + +/* + * These represent the new health value of a cell based on its neighboring + * values. The health is binary: either alive or dead. + */ +const uint8_t kIsAlive[] = { + 0, 0, 0, 1, 0, 0, 0, 0, 0, /* Values if the center cell is dead. */ + 0, 0, 1, 1, 0, 0, 0, 0, 0 /* Values if the center cell is alive. */ +}; + +void UpdateContext(uint32_t width, uint32_t height) { + if (width != g_Context.size.width || height != g_Context.size.height) { + size_t size = width * height; + size_t index; + + free(g_Context.cell_in); + free(g_Context.cell_out); + + /* Create a new context */ + g_Context.cell_in = (uint8_t*) malloc(size); + g_Context.cell_out = (uint8_t*) malloc(size); + + memset(g_Context.cell_out, 0, size); + for (index = 0; index < size; index++) { + g_Context.cell_in[index] = rand() & 1; + } + } + + /* Recreate the graphics context on a view change */ + g_pCore->ReleaseResource(g_Context.ctx); + g_Context.size.width = width; + g_Context.size.height = height; + g_Context.ctx = + g_pGraphics2D->Create(PSGetInstanceId(), &g_Context.size, PP_TRUE); + g_Context.bound = + g_pInstance->BindGraphics(PSGetInstanceId(), g_Context.ctx); +} + +void DrawCell(int32_t x, int32_t y) { + int32_t width = g_Context.size.width; + int32_t height = g_Context.size.height; + + if (!g_Context.cell_in) return; + + if (x > 0 && x < width - 1 && y > 0 && y < height - 1) { + g_Context.cell_in[x - 1 + y * width] = 1; + g_Context.cell_in[x + 1 + y * width] = 1; + g_Context.cell_in[x + (y - 1) * width] = 1; + g_Context.cell_in[x + (y + 1) * width] = 1; + } +} + + +void ProcessEvent(PSEvent* event) { + switch(event->type) { + /* If the view updates, build a new Graphics 2D Context */ + case PSE_INSTANCE_DIDCHANGEVIEW: { + struct PP_Rect rect; + + g_pView->GetRect(event->as_resource, &rect); + UpdateContext(rect.size.width, rect.size.height); + break; + } + + case PSE_INSTANCE_HANDLEINPUT: { + PP_InputEvent_Type type = g_pInputEvent->GetType(event->as_resource); + PP_InputEvent_Modifier modifiers = + g_pInputEvent->GetModifiers(event->as_resource); + + switch(type) { + case PP_INPUTEVENT_TYPE_MOUSEDOWN: { + struct PP_Point location = + g_pMouseInput->GetPosition(event->as_resource); + DrawCell(location.x, location.y); + break; + } + + case PP_INPUTEVENT_TYPE_MOUSEMOVE: { + struct PP_Point location = + g_pMouseInput->GetPosition(event->as_resource); + + /* If the button is down, draw */ + if (modifiers & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { + DrawCell(location.x, location.y); + } + break; + } + + case PP_INPUTEVENT_TYPE_KEYDOWN: { + PP_Bool fullscreen = g_pFullscreen->IsFullscreen(PSGetInstanceId()); + g_pFullscreen->SetFullscreen(PSGetInstanceId(), + fullscreen ? PP_FALSE : PP_TRUE); + break; + } + default: + break; + } + /* case PSE_INSTANCE_HANDLEINPUT */ + break; + } + + default: + break; + } +} + + +void Stir(uint32_t width, uint32_t height) { + int i; + if (g_Context.cell_in == NULL || g_Context.cell_out == NULL) + return; + + for (i = 0; i < width; ++i) { + g_Context.cell_in[i] = rand() & 1; + g_Context.cell_in[i + (height - 1) * width] = rand() & 1; + } + for (i = 0; i < height; ++i) { + g_Context.cell_in[i * width] = rand() & 1; + g_Context.cell_in[i * width + (width - 1)] = rand() & 1; + } +} + +void Render() { + struct PP_Size* psize = &g_Context.size; + PP_ImageDataFormat format = g_pImageData->GetNativeImageDataFormat(); + + /* + * Create a buffer to draw into. Since we are waiting until the next flush + * chrome has an opportunity to cache this buffer see ppb_graphics_2d.h. + */ + PP_Resource image = + g_pImageData->Create(PSGetInstanceId(), format, psize, PP_FALSE); + uint8_t* pixels = g_pImageData->Map(image); + + struct PP_ImageDataDesc desc; + uint8_t* cell_temp; + uint32_t x, y; + + /* If we somehow have not allocated these pointers yet, skip this frame. */ + if (!g_Context.cell_in || !g_Context.cell_out) return; + + /* Get the stride. */ + g_pImageData->Describe(image, &desc); + + /* Stir up the edges to prevent the simulation from reaching steady state. */ + Stir(desc.size.width, desc.size.height); + + /* Do neighbor summation; apply rules, output pixel color. */ + for (y = 1; y < desc.size.height - 1; ++y) { + uint8_t *src0 = (g_Context.cell_in + (y - 1) * desc.size.width) + 1; + uint8_t *src1 = src0 + desc.size.width; + uint8_t *src2 = src1 + desc.size.width; + int count; + uint32_t color; + uint8_t *dst = (g_Context.cell_out + y * desc.size.width) + 1; + uint32_t *pixel_line = (uint32_t*) (pixels + y * desc.stride); + + for (x = 1; x < (desc.size.width - 1); ++x) { + /* Build sum, weight center by 9x. */ + count = src0[-1] + src0[0] + src0[1] + + src1[-1] + src1[0] * 9 + src1[1] + + src2[-1] + src2[0] + src2[1]; + color = kNeighborColors[count]; + + *pixel_line++ = color; + *dst++ = kIsAlive[count]; + ++src0; + ++src1; + ++src2; + } + } + + cell_temp = g_Context.cell_in; + g_Context.cell_in = g_Context.cell_out; + g_Context.cell_out = cell_temp; + + /* Unmap the range, we no longer need it. */ + g_pImageData->Unmap(image); + + /* Replace the contexts, and block until it's on the screen. */ + g_pGraphics2D->ReplaceContents(g_Context.ctx, image); + g_pGraphics2D->Flush(g_Context.ctx, PP_BlockUntilComplete()); + + /* Release the image data, we no longer need it. */ + g_pCore->ReleaseResource(image); +} + +/* + * Starting point for the module. We do not use main since it would + * collide with main in libppapi_cpp. + */ +int example_main(int argc, const char *argv[]) { + fprintf(stdout,"Started main.\n"); + g_pCore = (PPB_Core*)PSGetInterface(PPB_CORE_INTERFACE); + g_pFullscreen = (PPB_Fullscreen*)PSGetInterface(PPB_FULLSCREEN_INTERFACE); + g_pGraphics2D = (PPB_Graphics2D*)PSGetInterface(PPB_GRAPHICS_2D_INTERFACE); + g_pInstance = (PPB_Instance*)PSGetInterface(PPB_INSTANCE_INTERFACE); + g_pImageData = (PPB_ImageData*)PSGetInterface(PPB_IMAGEDATA_INTERFACE); + g_pView = (PPB_View*)PSGetInterface(PPB_VIEW_INTERFACE); + + g_pInputEvent = + (PPB_InputEvent*) PSGetInterface(PPB_INPUT_EVENT_INTERFACE); + g_pKeyboardInput = (PPB_KeyboardInputEvent*) + PSGetInterface(PPB_KEYBOARD_INPUT_EVENT_INTERFACE); + g_pMouseInput = + (PPB_MouseInputEvent*) PSGetInterface(PPB_MOUSE_INPUT_EVENT_INTERFACE); + g_pTouchInput = + (PPB_TouchInputEvent*) PSGetInterface(PPB_TOUCH_INPUT_EVENT_INTERFACE); + + PSEventSetFilter(PSE_ALL); + while (1) { + /* Process all waiting events without blocking */ + PSEvent* event; + while ((event = PSEventTryAcquire()) != NULL) { + ProcessEvent(event); + PSEventRelease(event); + } + + /* Render a frame, blocking until complete. */ + if (g_Context.bound) { + Render(); + } + } + return 0; +} + +/* + * Register the function to call once the Instance Object is initialized. + * see: pappi_simple/ps_main.h + */ +PPAPI_SIMPLE_REGISTER_MAIN(example_main); diff --git a/native_client_sdk/src/examples/demo/voronoi/example.dsc b/native_client_sdk/src/examples/demo/voronoi/example.dsc new file mode 100644 index 0000000000..d9fabcebc9 --- /dev/null +++ b/native_client_sdk/src/examples/demo/voronoi/example.dsc @@ -0,0 +1,21 @@ +{ + 'TOOLS': ['newlib', 'glibc', 'pnacl'], + 'TARGETS': [ + { + 'NAME' : 'voronoi', + 'TYPE' : 'main', + 'SOURCES' : [ + 'voronoi.cc' + ], + + 'LIBS': ['sdk_util', 'ppapi_cpp', 'ppapi', 'pthread'] + } + ], + 'DATA': [ + 'example.js', + ], + 'DEST': 'examples/demo', + 'NAME': 'voronoi', + 'TITLE': 'Multi-Threaded Voronoi Demo', + 'GROUP': 'Demo' +} diff --git a/native_client_sdk/src/examples/demo/voronoi/example.js b/native_client_sdk/src/examples/demo/voronoi/example.js new file mode 100644 index 0000000000..aa02f38049 --- /dev/null +++ b/native_client_sdk/src/examples/demo/voronoi/example.js @@ -0,0 +1,59 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function moduleDidLoad() { +} + +function postThreadFunc(numThreads) { + return function () { + common.naclModule.postMessage('threads: ' + numThreads); + } +} + +// Add event listeners after the NaCl module has loaded. These listeners will +// forward messages to the NaCl module via postMessage() +function attachListeners() { + document.getElementById('benchmark').addEventListener('click', + function() { + common.naclModule.postMessage('run benchmark'); + common.updateStatus('BENCHMARKING... (please wait)'); + }); + document.getElementById('drawPoints').addEventListener('click', + function() { + var checked = document.getElementById('drawPoints').checked; + if (checked) + common.naclModule.postMessage('with points'); + else + common.naclModule.postMessage('without points'); + }); + document.getElementById('drawInteriors').addEventListener('click', + function() { + var checked = document.getElementById('drawInteriors').checked; + if (checked) + common.naclModule.postMessage('with interiors'); + else + common.naclModule.postMessage('without interiors'); + }); + var threads = [0, 1, 2, 4, 6, 8, 12, 16, 24, 32]; + for (var i = 0; i < threads.length; i++) { + document.getElementById('radio'+i).addEventListener('click', + postThreadFunc(threads[i])); + } + document.getElementById('pointRange').addEventListener('change', + function() { + var value = document.getElementById('pointRange').value; + common.naclModule.postMessage('points: ' + value); + document.getElementById('pointCount').textContent = value + ' points'; + }); +} + +// Handle a message coming from the NaCl module. +// In the Voronoi example, the only message will be the benchmark result. +function handleMessage(message_event) { + var x = (Math.round(message_event.data * 1000) / 1000).toFixed(3); + document.getElementById('result').textContent = + 'Result: ' + x + ' seconds'; + common.updateStatus('SUCCESS') +} + diff --git a/native_client_sdk/src/examples/demo/voronoi/index.html b/native_client_sdk/src/examples/demo/voronoi/index.html new file mode 100644 index 0000000000..7cbbd19deb --- /dev/null +++ b/native_client_sdk/src/examples/demo/voronoi/index.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html> +<!-- +Copyright (c) 2013 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<head> + <meta http-equiv="Pragma" content="no-cache"> + <meta http-equiv="Expires" content="-1"> + <title>{{title}}</title> + <script type="text/javascript" src="common.js"></script> + <script type="text/javascript" src="example.js"></script> +</head> +<body {{attrs}} data-width="512" data-height="512"> + <h1>{{title}}</h1> + <h2>Status: <code id="statusField">NO-STATUS</code></h2> + <div> + This demo renders the Voronoi diagram for a moving set of points using a + brute force technique. + <br> + Number of points: + <input type="range" id="pointRange" + min="1" max="1024" step="1" value="256" /> + <label id="pointCount" >256 points</label> + <br> + Number of threads (0 is main thread): + <input type="radio" name="threadCount" id="radio0" value="0" + checked="checked"> + <label for="radio0">0</label> + <input type="radio" name="threadCount" id="radio1" value="1"> + <label for="radio1">1</label> + <input type="radio" name="threadCount" id="radio2" value="2"> + <label for="radio2">2</label> + <input type="radio" name="threadCount" id="radio3" value="4"> + <label for="radio3">4</label> + <input type="radio" name="threadCount" id="radio4" value="6"> + <label for="radio4">6</label> + <input type="radio" name="threadCount" id="radio5" value="8"> + <label for="radio5">8</label> + <input type="radio" name="threadCount" id="radio6" value="12"> + <label for="radio6">12</label> + <input type="radio" name="threadCount" id="radio7" value="16"> + <label for="radio7">16</label> + <input type="radio" name="threadCount" id="radio8" value="24"> + <label for="radio8">24</label> + <input type="radio" name="threadCount" id="radio9" value="32"> + <label for="radio9">32</label> + <br> + <input type="checkbox" id="drawPoints" checked="checked"> + <label for="draw_points">Draw Points</label> + <input type="checkbox" id="drawInteriors" checked="checked"> + <label for="draw_interiors">Draw Interiors</label> + <input type="submit" id="benchmark" value="Run Benchmark"> + <label id="result" name="result"> </label> + </div> + <!-- The NaCl plugin will be embedded inside the element with id "listener". + See common.js.--> + <div id="listener"></div> +</body> +</html> diff --git a/native_client_sdk/src/examples/demo/voronoi/voronoi.cc b/native_client_sdk/src/examples/demo/voronoi/voronoi.cc new file mode 100644 index 0000000000..d4e1025181 --- /dev/null +++ b/native_client_sdk/src/examples/demo/voronoi/voronoi.cc @@ -0,0 +1,582 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <assert.h> +#include <math.h> +#include <ppapi/cpp/completion_callback.h> +#include <ppapi/cpp/graphics_2d.h> +#include <ppapi/cpp/image_data.h> +#include <ppapi/cpp/input_event.h> +#include <ppapi/cpp/instance.h> +#include <ppapi/cpp/module.h> +#include <ppapi/cpp/rect.h> +#include <ppapi/cpp/size.h> +#include <ppapi/cpp/var.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <unistd.h> + +#include <algorithm> +#include <string> + +#include "sdk_util/thread_pool.h" + +// Global properties used to setup Voronoi demo. +namespace { +const int kMinRectSize = 4; +const int kStartRecurseSize = 32; // must be power-of-two +const float kHugeZ = 1.0e38f; +const float kPI = M_PI; +const float kTwoPI = kPI * 2.0f; +const int kFramesToBenchmark = 100; +const unsigned int kRandomStartSeed = 0xC0DE533D; +const int kMaxPointCount = 1024; +const int kStartPointCount = 256; + +unsigned int g_rand_state = kRandomStartSeed; + +// random number helper +inline unsigned char rand255() { + return static_cast<unsigned char>(rand_r(&g_rand_state) & 255); +} + +// random number helper +inline float frand() { + return (static_cast<float>(rand_r(&g_rand_state)) / + static_cast<float>(RAND_MAX)); +} + +// reset random seed +inline void rand_reset(unsigned int seed) { + g_rand_state = seed; +} + +// returns true if input is power of two. +inline bool is_pow2(int x) { + return (x & (x - 1)) == 0; +} + +inline double getseconds() { + const double usec_to_sec = 0.000001; + timeval tv; + if (0 == gettimeofday(&tv, NULL)) + return tv.tv_sec + tv.tv_usec * usec_to_sec; + return 0.0; +} + +inline uint32_t MakeRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { + return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)); +} +} // namespace + +// Vec2, simple 2D vector +struct Vec2 { + float x, y; + Vec2() {} + Vec2(float px, float py) { + x = px; + y = py; + } + void Set(float px, float py) { + x = px; + y = py; + } +}; + +// The main object that runs Voronoi simulation. +class Voronoi : public pp::Instance { + public: + explicit Voronoi(PP_Instance instance); + virtual ~Voronoi(); + + virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { + return true; + } + + virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip); + + // Catch events. + virtual bool HandleInputEvent(const pp::InputEvent& event); + + // Catch messages posted from Javascript. + virtual void HandleMessage(const pp::Var& message); + + private: + // Methods prefixed with 'w' are run on worker threads. + int wCell(float x, float y); + inline void wFillSpan(uint32_t *pixels, uint32_t color, int width); + void wRenderTile(int x, int y, int w, int h); + void wProcessTile(int x, int y, int w, int h); + void wSubdivide(int x, int y, int w, int h); + void wMakeRect(int region, int *x, int *y, int *w, int *h); + bool wTestRect(int *m, int x, int y, int w, int h); + void wFillRect(int x, int y, int w, int h, uint32_t color); + void wRenderRect(int x0, int y0, int x1, int y1); + void wRenderRegion(int region); + static void wRenderRegionEntry(int region, void *thiz); + + // These methods are only called by the main thread. + void Reset(); + void UpdateSim(); + void RenderDot(float x, float y, uint32_t color1, uint32_t color2); + void SuperimposePositions(); + void Render(); + void Draw(); + void StartBenchmark(); + void EndBenchmark(); + + // Runs a tick of the simulations, updating all buffers. Flushes the + // contents of |image_data_| to the 2D graphics context. + void Update(); + + // Create and initialize the 2D context used for drawing. + void CreateContext(const pp::Size& size); + // Destroy the 2D drawing context. + void DestroyContext(); + // Push the pixels to the browser, then attempt to flush the 2D context. + void FlushPixelBuffer(); + static void FlushCallback(void* data, int32_t result); + + + pp::Graphics2D* graphics_2d_context_; + pp::ImageData* image_data_; + Vec2 positions_[kMaxPointCount]; + Vec2 screen_positions_[kMaxPointCount]; + Vec2 velocities_[kMaxPointCount]; + uint32_t colors_[kMaxPointCount]; + float ang_; + int point_count_; + int num_threads_; + const int num_regions_; + bool draw_points_; + bool draw_interiors_; + ThreadPool* workers_; + int width_; + int height_; + uint32_t stride_in_pixels_; + uint32_t* pixel_buffer_; + int benchmark_frame_counter_; + bool benchmarking_; + double benchmark_start_time_; + double benchmark_end_time_; +}; + + + +void Voronoi::Reset() { + rand_reset(kRandomStartSeed); + ang_ = 0.0f; + for (int i = 0; i < kMaxPointCount; i++) { + // random initial start position + const float x = frand(); + const float y = frand(); + positions_[i].Set(x, y); + // random directional velocity ( -1..1, -1..1 ) + const float speed = 0.0005f; + const float u = (frand() * 2.0f - 1.0f) * speed; + const float v = (frand() * 2.0f - 1.0f) * speed; + velocities_[i].Set(u, v); + // 'unique' color (well... unique enough for our purposes) + colors_[i] = MakeRGBA(rand255(), rand255(), rand255(), 255); + } +} + +Voronoi::Voronoi(PP_Instance instance) : pp::Instance(instance), + graphics_2d_context_(NULL), + image_data_(NULL), + num_regions_(256) { + draw_points_ = true; + draw_interiors_ = true; + width_ = 0; + height_ = 0; + stride_in_pixels_ = 0; + pixel_buffer_ = NULL; + benchmark_frame_counter_ = 0; + benchmarking_ = false; + + point_count_ = kStartPointCount; + Reset(); + + // By default, render from the dispatch thread. + num_threads_ = 0; + workers_ = new ThreadPool(num_threads_); + + // Request PPAPI input events for mouse & keyboard. + RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE); + RequestInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); +} + +Voronoi::~Voronoi() { + delete workers_; + DestroyContext(); +} + +// This is the core of the Voronoi calculation. At a given point on the +// screen, iterate through all voronoi positions and render them as 3D cones. +// We're looking for the voronoi cell that generates the closest z value. +// (not really cones - since it is all relative, we avoid doing the +// expensive sqrt and test against z*z instead) +// If multithreading, this function is only called by the worker threads. +int Voronoi::wCell(float x, float y) { + int closest_cell = 0; + float zz = kHugeZ; + Vec2* pos = screen_positions_; + for (int i = 0; i < point_count_; ++i) { + // measured 5.18 cycles per iteration on a core2 + float dx = x - pos[i].x; + float dy = y - pos[i].y; + float dd = (dx * dx + dy * dy); + if (dd < zz) { + zz = dd; + closest_cell = i; + } + } + return closest_cell; +} + +// Given a region r, derive a non-overlapping rectangle for a thread to +// work on. +// If multithreading, this function is only called by the worker threads. +void Voronoi::wMakeRect(int r, int* x, int* y, int* w, int* h) { + const int parts = 16; + assert(parts * parts == num_regions_); + *w = width_ / parts; + *h = height_ / parts; + *x = *w * (r % parts); + *y = *h * ((r / parts) % parts); +} + +// Test 4 corners of a rectangle to see if they all belong to the same +// voronoi cell. Each test is expensive so bail asap. Returns true +// if all 4 corners match. +// If multithreading, this function is only called by the worker threads. +bool Voronoi::wTestRect(int* m, int x, int y, int w, int h) { + // each test is expensive, so exit ASAP + const int m0 = wCell(x, y); + const int m1 = wCell(x + w - 1, y); + if (m0 != m1) return false; + const int m2 = wCell(x, y + h - 1); + if (m0 != m2) return false; + const int m3 = wCell(x + w - 1, y + h - 1); + if (m0 != m3) return false; + // all 4 corners belong to the same cell + *m = m0; + return true; +} + +// Quickly fill a span of pixels with a solid color. Assumes +// span width is divisible by 4. +// If multithreading, this function is only called by the worker threads. +inline void Voronoi::wFillSpan(uint32_t* pixels, uint32_t color, int width) { + if (!draw_interiors_) { + const uint32_t gray = MakeRGBA(128, 128, 128, 255); + color = gray; + } + for (int i = 0; i < width; i += 4) { + *pixels++ = color; + *pixels++ = color; + *pixels++ = color; + *pixels++ = color; + } +} + +// Quickly fill a rectangle with a solid color. Assumes +// the width w parameter is evenly divisible by 4. +// If multithreading, this function is only called by the worker threads. +void Voronoi::wFillRect(int x, int y, int w, int h, uint32_t color) { + const uint32_t pitch = width_; + uint32_t* pixels = pixel_buffer_ + y * pitch + x; + for (int j = 0; j < h; j++) { + wFillSpan(pixels, color, w); + pixels += pitch; + } +} + +// When recursive subdivision reaches a certain minimum without finding a +// rectangle that has four matching corners belonging to the same voronoi +// cell, this function will break the retangular 'tile' into smaller scanlines +// and look for opportunities to quick fill at the scanline level. If the +// scanline can't be quick filled, it will slow down even further and compute +// voronoi membership per pixel. +void Voronoi::wRenderTile(int x, int y, int w, int h) { + // rip through a tile + uint32_t* pixels = pixel_buffer_ + y * stride_in_pixels_ + x; + for (int j = 0; j < h; j++) { + // get start and end cell values + int ms = wCell(x + 0, y + j); + int me = wCell(x + w - 1, y + j); + // if the end points are the same, quick fill the span + if (ms == me) { + wFillSpan(pixels, colors_[ms], w); + } else { + // else compute each pixel in the span... this is the slow part! + uint32_t* p = pixels; + *p++ = colors_[ms]; + for (int i = 1; i < (w - 1); i++) { + int m = wCell(x + i, y + j); + *p++ = colors_[m]; + } + *p++ = colors_[me]; + } + pixels += stride_in_pixels_; + } +} + +// Take a rectangular region and do one of - +// If all four corners below to the same voronoi cell, stop recursion and +// quick fill the rectangle. +// If the minimum rectangle size has been reached, break out of recursion +// and process the rectangle. This small rectangle is called a tile. +// Otherwise, keep recursively subdividing the rectangle into 4 equally +// sized smaller rectangles. +// Note: at the moment, these will always be squares, not rectangles. +// If multithreading, this function is only called by the worker threads. +void Voronoi::wSubdivide(int x, int y, int w, int h) { + int m; + // if all 4 corners are equal, quick fill interior + if (wTestRect(&m, x, y, w, h)) { + wFillRect(x, y, w, h, colors_[m]); + } else { + // did we reach the minimum rectangle size? + if ((w <= kMinRectSize) || (h <= kMinRectSize)) { + wRenderTile(x, y, w, h); + } else { + // else recurse into smaller rectangles + const int half_w = w / 2; + const int half_h = h / 2; + wSubdivide(x, y, half_w, half_h); + wSubdivide(x + half_w, y, half_w, half_h); + wSubdivide(x, y + half_h, half_w, half_h); + wSubdivide(x + half_w, y + half_h, half_w, half_h); + } + } +} + +// This function cuts up the rectangle into power of 2 sized squares. It +// assumes the input rectangle w & h are evenly divisible by +// kStartRecurseSize. +// If multithreading, this function is only called by the worker threads. +void Voronoi::wRenderRect(int x, int y, int w, int h) { + for (int iy = y; iy < (y + h); iy += kStartRecurseSize) { + for (int ix = x; ix < (x + w); ix += kStartRecurseSize) { + wSubdivide(ix, iy, kStartRecurseSize, kStartRecurseSize); + } + } +} + +// If multithreading, this function is only called by the worker threads. +void Voronoi::wRenderRegion(int region) { + // convert region # into x0, y0, x1, y1 rectangle + int x, y, w, h; + wMakeRect(region, &x, &y, &w, &h); + // render this rectangle + wRenderRect(x, y, w, h); +} + +// Entry point for worker thread. Can't pass a member function around, so we +// have to do this little round-about. +void Voronoi::wRenderRegionEntry(int region, void* thiz) { + static_cast<Voronoi*>(thiz)->wRenderRegion(region); +} + +// Function Voronoi::UpdateSim() +// Run a simple sim to move the voronoi positions. This update loop +// is run once per frame. Called from the main thread only, and only +// when the worker threads are idle. +void Voronoi::UpdateSim() { + ang_ += 0.002f; + if (ang_ > kTwoPI) { + ang_ = ang_ - kTwoPI; + } + float z = cosf(ang_) * 3.0f; + // push the points around on the screen for animation + for (int j = 0; j < kMaxPointCount; j++) { + positions_[j].x += (velocities_[j].x) * z; + positions_[j].y += (velocities_[j].y) * z; + screen_positions_[j].x = positions_[j].x * width_; + screen_positions_[j].y = positions_[j].y * height_; + } +} + +// Renders a small diamond shaped dot at x, y clipped against the window +void Voronoi::RenderDot(float x, float y, uint32_t color1, uint32_t color2) { + const int ix = static_cast<int>(x); + const int iy = static_cast<int>(y); + // clip it against window + if (ix < 1) return; + if (ix >= (width_ - 1)) return; + if (iy < 1) return; + if (iy >= (height_ - 1)) return; + uint32_t* pixel = pixel_buffer_ + iy * stride_in_pixels_ + ix; + // render dot as a small diamond + *pixel = color1; + *(pixel - 1) = color2; + *(pixel + 1) = color2; + *(pixel - stride_in_pixels_) = color2; + *(pixel + stride_in_pixels_) = color2; +} + +// Superimposes dots on the positions. +void Voronoi::SuperimposePositions() { + const uint32_t white = MakeRGBA(255, 255, 255, 255); + const uint32_t gray = MakeRGBA(192, 192, 192, 255); + for (int i = 0; i < point_count_; i++) { + RenderDot( + screen_positions_[i].x, screen_positions_[i].y, white, gray); + } +} + +// Renders the Voronoi diagram, dispatching the work to multiple threads. +// Note: This Dispatch() is from the main PPAPI thread, so care must be taken +// not to attempt PPAPI calls from the worker threads, since Dispatch() will +// block here until all work is complete. The worker threads are compute only +// and do not make any PPAPI calls. +void Voronoi::Render() { + workers_->Dispatch(num_regions_, wRenderRegionEntry, this); + if (draw_points_) + SuperimposePositions(); +} + +void Voronoi::DidChangeView(const pp::Rect& position, const pp::Rect& clip) { + if (position.size().width() == width_ && + position.size().height() == height_) + return; // Size didn't change, no need to update anything. + + // Create a new device context with the new size. + DestroyContext(); + CreateContext(position.size()); + Update(); +} + +void Voronoi::StartBenchmark() { + Reset(); + printf("Benchmark started...\n"); + benchmark_frame_counter_ = kFramesToBenchmark; + benchmarking_ = true; + benchmark_start_time_ = getseconds(); +} + +void Voronoi::EndBenchmark() { + benchmark_end_time_ = getseconds(); + printf("Benchmark ended... time: %2.5f\n", + benchmark_end_time_ - benchmark_start_time_); + benchmarking_ = false; + benchmark_frame_counter_ = 0; + pp::Var result(benchmark_end_time_ - benchmark_start_time_); + PostMessage(result); +} + +// Handle input events from the user. +bool Voronoi::HandleInputEvent(const pp::InputEvent& event) { + switch (event.GetType()) { + case PP_INPUTEVENT_TYPE_KEYDOWN: { + pp::KeyboardInputEvent key = pp::KeyboardInputEvent(event); + uint32_t key_code = key.GetKeyCode(); + if (key_code == 84) // 't' key + if (!benchmarking_) + StartBenchmark(); + break; + } + default: + return false; + } + return true; +} + +// Handle messages sent from Javascript. +void Voronoi::HandleMessage(const pp::Var& message) { + if (message.is_string()) { + std::string message_string = message.AsString(); + if (message_string == "run benchmark" && !benchmarking_) + StartBenchmark(); + else if (message_string == "with points") + draw_points_ = true; + else if (message_string == "without points") + draw_points_ = false; + else if (message_string == "with interiors") + draw_interiors_ = true; + else if (message_string == "without interiors") + draw_interiors_ = false; + else if (strstr(message_string.c_str(), "points:")) { + int num_points = atoi(strstr(message_string.c_str(), " ")); + point_count_ = std::min(kMaxPointCount, std::max(0, num_points)); + } else if (strstr(message_string.c_str(), "threads:")) { + int thread_count = atoi(strstr(message_string.c_str(), " ")); + delete workers_; + workers_ = new ThreadPool(thread_count); + } + } +} + +void Voronoi::FlushCallback(void* thiz, int32_t result) { + static_cast<Voronoi*>(thiz)->Update(); +} + +// Update the 2d region and flush to make it visible on the page. +void Voronoi::FlushPixelBuffer() { + graphics_2d_context_->PaintImageData(*image_data_, pp::Point(0, 0)); + graphics_2d_context_->Flush(pp::CompletionCallback(&FlushCallback, this)); +} + +void Voronoi::Update() { + // Don't call FlushPixelBuffer() when benchmarking - vsync is enabled by + // default, and will throttle the benchmark results. + do { + UpdateSim(); + Render(); + if (!benchmarking_) break; + --benchmark_frame_counter_; + } while (benchmark_frame_counter_ > 0); + if (benchmarking_) + EndBenchmark(); + FlushPixelBuffer(); +} + +void Voronoi::CreateContext(const pp::Size& size) { + graphics_2d_context_ = new pp::Graphics2D(this, size, false); + if (!BindGraphics(*graphics_2d_context_)) + printf("Couldn't bind the device context\n"); + image_data_ = new pp::ImageData(this, + PP_IMAGEDATAFORMAT_BGRA_PREMUL, + size, + false); + width_ = image_data_->size().width(); + height_ = image_data_->size().height(); + // This demo requires power of two width & height buffers. + assert(is_pow2(width_) && is_pow2(height_)); + stride_in_pixels_ = static_cast<uint32_t>(image_data_->stride() / 4); + pixel_buffer_ = static_cast<uint32_t*>(image_data_->data()); +} + +void Voronoi::DestroyContext() { + delete graphics_2d_context_; + delete image_data_; + graphics_2d_context_ = NULL; + image_data_ = NULL; + width_ = 0; + height_ = 0; + stride_in_pixels_ = 0; + pixel_buffer_ = NULL; +} + +class VoronoiModule : public pp::Module { + public: + VoronoiModule() : pp::Module() {} + virtual ~VoronoiModule() {} + + // Create and return a Voronoi instance. + virtual pp::Instance* CreateInstance(PP_Instance instance) { + return new Voronoi(instance); + } +}; + +namespace pp { +Module* CreateModule() { + return new VoronoiModule(); +} +} // namespace pp + diff --git a/native_client_sdk/src/examples/getting_started/hello_world_ppapi_main/example.dsc b/native_client_sdk/src/examples/getting_started/hello_world_ppapi_main/example.dsc deleted file mode 100644 index 19a2b4173a..0000000000 --- a/native_client_sdk/src/examples/getting_started/hello_world_ppapi_main/example.dsc +++ /dev/null @@ -1,18 +0,0 @@ -{ - 'TOOLS': ['newlib', 'glibc', 'pnacl'], - 'TARGETS': [ - { - 'NAME': 'hello_world_stdio', - 'TYPE': 'main', - 'SOURCES': ['hello_world.c'], - 'LIBS': ['ppapi_main', 'nacl_io', 'ppapi_cpp', 'ppapi', 'pthread'] - } - ], - 'DATA': [ - 'example.js', - ], - 'DEST': 'examples/getting_started', - 'NAME': 'hello_world_ppapi_main', - 'TITLE': 'Hello World (libppapi_main)', - 'GROUP': 'Getting Started' -} diff --git a/native_client_sdk/src/examples/getting_started/hello_world_ppapi_main/hello_world.c b/native_client_sdk/src/examples/getting_started/hello_world_ppapi_main/hello_world.c deleted file mode 100644 index 1d3d6f10ff..0000000000 --- a/native_client_sdk/src/examples/getting_started/hello_world_ppapi_main/hello_world.c +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include <stdio.h> -#include <string.h> - -#include "ppapi/c/ppb_var.h" -#include "ppapi/c/ppb_messaging.h" -#include "ppapi/c/ppb_console.h" - -#include "ppapi_main/ppapi_main.h" - -// The default arguments to PPAPI_MAIN maps: -// STDIN -> /dev/stdin -// STDOUT -> /dev/stdout -// STDERR -> /dev/console3 -// We use our own args here so that stdout sends messages to JavaScript via -// PostMessage (/dev/tty). -PPAPI_MAIN_WITH_ARGS("pm_stdout", "/dev/tty", NULL, NULL) - -// -// The "main" entry point called by PPAPIInstance once initialization -// takes place. This is called off the main thread, which is hidden -// from the developer, making it safe to use blocking calls. -// The arguments are provided as: -// argv[0] = "NEXE" -// argv[1] = "--<KEY>" -// argv[2] = "<VALUE>" -// Where the embed tag for this module uses KEY=VALUE -// -int ppapi_main(int argc, const char* argv[]) { - int index = 1; - - // Use PostMessage to send "Hello World" to JavaScript. - printf("Hello World STDOUT.\n"); - - // Use PPAPI Console interface to send "Hello World" to the - // JavaScript Console. - fprintf(stderr, "Hello World STDERR.\n"); - - // Print the arguments we received from the web page - printf("NAME: %s\n", argv[0]); - while (index + 2 < argc) { - printf(" ARGS: %s=%s\n", argv[index + 0], argv[index + 1]); - index += 2; - } - return 0; -} diff --git a/native_client_sdk/src/examples/getting_started/simple_hello_world/example.dsc b/native_client_sdk/src/examples/getting_started/simple_hello_world/example.dsc new file mode 100644 index 0000000000..de8c6a35f7 --- /dev/null +++ b/native_client_sdk/src/examples/getting_started/simple_hello_world/example.dsc @@ -0,0 +1,19 @@ +{ + 'TOOLS': ['newlib', 'glibc', 'pnacl'], + 'TARGETS': [ + { + 'NAME' : 'simple_hello_world', + 'TYPE' : 'main', + 'SOURCES' : ['hello_world.c'], + 'LIBS': ['ppapi_simple', 'nacl_io', 'ppapi_cpp', 'ppapi', 'pthread'] + } + ], + 'DATA': [ + 'example.js', + ], + 'DEST': 'examples/getting_started', + 'NAME': 'simple_hello_world', + 'TITLE': 'Hello World (ppapi_simple)', + 'GROUP': 'Getting Started' +} + diff --git a/native_client_sdk/src/examples/getting_started/hello_world_ppapi_main/example.js b/native_client_sdk/src/examples/getting_started/simple_hello_world/example.js index 7f269215fe..7f269215fe 100644 --- a/native_client_sdk/src/examples/getting_started/hello_world_ppapi_main/example.js +++ b/native_client_sdk/src/examples/getting_started/simple_hello_world/example.js diff --git a/native_client_sdk/src/examples/getting_started/simple_hello_world/hello_world.c b/native_client_sdk/src/examples/getting_started/simple_hello_world/hello_world.c new file mode 100644 index 0000000000..082c62d4ea --- /dev/null +++ b/native_client_sdk/src/examples/getting_started/simple_hello_world/hello_world.c @@ -0,0 +1,24 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <stdio.h> +#include <string.h> + +#include "ppapi_simple/ps_main.h" + +int example_main(int argc, const char* argv[]) { + /* Use ppb_messaging to send "Hello World" to JavaScript. */ + printf("Hello World STDOUT.\n"); + + /* Use ppb_console send "Hello World" to the JavaScript Console. */ + fprintf(stderr, "Hello World STDERR.\n"); + return 0; +} + +/* + * Register the function to call once the Instance Object is initialized. + * see: pappi_simple/ps_main.h + */ +PPAPI_SIMPLE_REGISTER_MAIN(example_main) diff --git a/native_client_sdk/src/examples/getting_started/hello_world_ppapi_main/index.html b/native_client_sdk/src/examples/getting_started/simple_hello_world/index.html index c6a4aa1ef4..c6a4aa1ef4 100644 --- a/native_client_sdk/src/examples/getting_started/hello_world_ppapi_main/index.html +++ b/native_client_sdk/src/examples/getting_started/simple_hello_world/index.html diff --git a/native_client_sdk/src/examples/hello_world_instance3d/example.dsc b/native_client_sdk/src/examples/hello_world_instance3d/example.dsc deleted file mode 100644 index 8dda62375e..0000000000 --- a/native_client_sdk/src/examples/hello_world_instance3d/example.dsc +++ /dev/null @@ -1,27 +0,0 @@ -{ - 'DISABLE': True, - 'TOOLS': ['newlib', 'glibc', 'pnacl'], - 'TARGETS': [ - { - 'NAME' : 'hello_world_instance3d', - 'TYPE' : 'main', - 'SOURCES' : ['hello_world.cc', 'matrix.cc', 'matrix.h'], - 'CXXFLAGS': [ - '-I../../src', - '-I../../src/ppapi/lib/gl' - ], - 'DEPS': ['ppapi_main', 'nacl_io'], - 'LIBS': ['ppapi_gles2', 'ppapi_cpp', 'ppapi', - 'pthread'] - } - ], - 'DATA': [ - 'fragment_shader_es2.frag', - 'hello.raw', - 'vertex_shader_es2.vert' - ], - 'DEST': 'examples', - 'NAME': 'hello_world_instance3d', - 'TITLE': 'Hello World GLES 2.0 using ppapi_instance3d', - 'GROUP': 'API' -} diff --git a/native_client_sdk/src/examples/hello_world_instance3d/fragment_shader_es2.frag b/native_client_sdk/src/examples/hello_world_instance3d/fragment_shader_es2.frag deleted file mode 100644 index 247c559f8c..0000000000 --- a/native_client_sdk/src/examples/hello_world_instance3d/fragment_shader_es2.frag +++ /dev/null @@ -1,8 +0,0 @@ -precision mediump float; -varying vec3 v_color; -varying vec2 v_texCoord; -uniform sampler2D s_texture; -void main() -{ - gl_FragColor = texture2D( s_texture, vec2(v_texCoord.x,1.0 - v_texCoord.y) ) + vec4(v_color.x,v_color.y,v_color.z,1); -}
\ No newline at end of file diff --git a/native_client_sdk/src/examples/hello_world_instance3d/hello.raw b/native_client_sdk/src/examples/hello_world_instance3d/hello.raw Binary files differdeleted file mode 100644 index 3320575072..0000000000 --- a/native_client_sdk/src/examples/hello_world_instance3d/hello.raw +++ /dev/null diff --git a/native_client_sdk/src/examples/hello_world_instance3d/hello_world.cc b/native_client_sdk/src/examples/hello_world_instance3d/hello_world.cc deleted file mode 100644 index 26515b7249..0000000000 --- a/native_client_sdk/src/examples/hello_world_instance3d/hello_world.cc +++ /dev/null @@ -1,325 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/** @file hello_world_gles.cc - * This example demonstrates loading and running a simple 3D openGL ES 2.0 - * application. - */ - -//--------------------------------------------------------------------------- -// The spinning Cube -//--------------------------------------------------------------------------- - -#define _USE_MATH_DEFINES 1 -#include <fcntl.h> -#include <limits.h> -#include <math.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "nacl_io/nacl_io.h" - -#include "ppapi/c/pp_completion_callback.h" -#include "ppapi/c/pp_errors.h" -#include "ppapi/c/pp_instance.h" -#include "ppapi/c/pp_module.h" -#include "ppapi/c/pp_stdint.h" -#include "ppapi/c/pp_var.h" -#include "ppapi/c/ppb.h" -#include "ppapi/c/ppb_opengles2.h" -#include "ppapi/c/ppb_var.h" -#include "ppapi/c/ppp.h" -#include "ppapi/c/ppp_graphics_3d.h" -#include "ppapi/c/ppp_instance.h" - -#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h" - -#include "ppapi_main/ppapi_event.h" -#include "ppapi_main/ppapi_instance3d.h" -#include "ppapi_main/ppapi_main.h" - -#include <GLES2/gl2.h> -#include "matrix.h" - -GLuint g_positionLoc; -GLuint g_texCoordLoc; -GLuint g_colorLoc; -GLuint g_MVPLoc; -GLuint g_vboID; -GLuint g_ibID; -GLubyte g_Indices[36]; - -GLuint g_programObj; -GLuint g_vertexShader; -GLuint g_fragmentShader; - -GLuint g_textureLoc = 0; -GLuint g_textureID = 0; - -float g_fSpinX = 0.0f; -float g_fSpinY = 0.0f; - -//---------------------------------------------------------------------------- -// Rendering Assets -//---------------------------------------------------------------------------- -struct Vertex { - float tu, tv; - float color[3]; - float loc[3]; -}; - -Vertex* g_quadVertices = NULL; -const char* g_TextureData = NULL; -const char* g_VShaderData = NULL; -const char* g_FShaderData = NULL; - -bool g_Loaded = false; -bool g_Ready = false; - -float g_xSpin = 2.0f; -float g_ySpin = 0.5f; - -GLuint compileShader(GLenum type, const char* data) { - const char* shaderStrings[1]; - shaderStrings[0] = data; - - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, shaderStrings, NULL); - glCompileShader(shader); - return shader; -} - -void InitProgram(void) { - g_vertexShader = compileShader(GL_VERTEX_SHADER, g_VShaderData); - g_fragmentShader = compileShader(GL_FRAGMENT_SHADER, g_FShaderData); - - g_programObj = glCreateProgram(); - glAttachShader(g_programObj, g_vertexShader); - glAttachShader(g_programObj, g_fragmentShader); - glLinkProgram(g_programObj); - - glGenBuffers(1, &g_vboID); - glBindBuffer(GL_ARRAY_BUFFER, g_vboID); - glBufferData(GL_ARRAY_BUFFER, - 24 * sizeof(Vertex), - (void*)&g_quadVertices[0], - GL_STATIC_DRAW); - - glGenBuffers(1, &g_ibID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ibID); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - 36 * sizeof(char), - (void*)&g_Indices[0], - GL_STATIC_DRAW); - - // - // Create a texture to test out our fragment shader... - // - glGenTextures(1, &g_textureID); - glBindTexture(GL_TEXTURE_2D, g_textureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGB, GL_UNSIGNED_BYTE, - g_TextureData); - - // - // Locate some parameters by name so we can set them later... - // - g_textureLoc = glGetUniformLocation(g_programObj, "arrowTexture"); - g_positionLoc = glGetAttribLocation(g_programObj, "a_position"); - g_texCoordLoc = glGetAttribLocation(g_programObj, "a_texCoord"); - g_colorLoc = glGetAttribLocation(g_programObj, "a_color"); - g_MVPLoc = glGetUniformLocation(g_programObj, "a_MVP"); - printf("Program initialized.\n"); -} - -void BuildQuad(Vertex* verts, int axis[3], float depth, float color[3]) { - static float X[4] = { -1.0f, 1.0f, 1.0f, -1.0f }; - static float Y[4] = { -1.0f, -1.0f, 1.0f, 1.0f }; - - for (int i = 0; i < 4; i++) { - verts[i].tu = (1.0 - X[i]) / 2.0f; - verts[i].tv = (Y[i] + 1.0f) / -2.0f * depth; - verts[i].loc[axis[0]] = X[i] * depth; - verts[i].loc[axis[1]] = Y[i] * depth; - verts[i].loc[axis[2]] = depth; - for (int j = 0; j < 3; j++) - verts[i].color[j] = color[j] * (Y[i] + 1.0f) / 2.0f; - } -} - -Vertex* BuildCube() { - Vertex* verts = new Vertex[24]; - for (int i = 0; i < 3; i++) { - int Faxis[3]; - int Baxis[3]; - float Fcolor[3]; - float Bcolor[3]; - for (int j = 0; j < 3; j++) { - Faxis[j] = (j + i) % 3; - Baxis[j] = (j + i) % 3; - } - memset(Fcolor, 0, sizeof(float) * 3); - memset(Bcolor, 0, sizeof(float) * 3); - Fcolor[i] = 0.5f; - Bcolor[i] = 1.0f; - BuildQuad(&verts[0 + i * 4], Faxis, 1.0f, Fcolor); - BuildQuad(&verts[12 + i * 4], Baxis, -1.0f, Bcolor); - } - - for (int i = 0; i < 6; i++) { - g_Indices[i * 6 + 0] = 2 + i * 4; - g_Indices[i * 6 + 1] = 1 + i * 4; - g_Indices[i * 6 + 2] = 0 + i * 4; - g_Indices[i * 6 + 3] = 3 + i * 4; - g_Indices[i * 6 + 4] = 2 + i * 4; - g_Indices[i * 6 + 5] = 0 + i * 4; - } - return verts; -} - -static float clamp(float val, float min, float max) { - if (val < min) - return min; - if (val > max) - return max; - return val; -} - -void ProcessEvent(PPAPIEvent* event) { - if (event->event_type == PP_INPUTEVENT_TYPE_MOUSEMOVE) { - PPAPIMouseEvent* mouse = (PPAPIMouseEvent*)event; - g_ySpin = clamp((float) mouse->delta.x / 2, -4.0, 4.0); - g_xSpin = clamp((float) mouse->delta.y / 2, -4.0, 4.0); - } - if (event->event_type == PP_INPUTEVENT_TYPE_KEYUP) { - PPAPIKeyEvent* key = (PPAPIKeyEvent*)event; - if (key->key_code == 13) { - PPAPIInstance3D::GetInstance3D()->ToggleFullscreen(); - } - } -} - -void PPAPIRender(PP_Resource ctx, uint32_t width, uint32_t height) { - if (!g_Ready) { - if (g_Loaded) { - InitProgram(); - g_Ready = true; - } else { - return; - } - } - - PPAPIEvent* event; - while (PPAPIEvent* event = PPAPI_AcquireEvent()) { - ProcessEvent(event); - PPAPI_ReleaseEvent(event); - } - - static float xRot = 0.0; - static float yRot = 0.0; - - xRot -= g_xSpin; - yRot -= g_ySpin; - - if (xRot >= 360.0f) - xRot = 0.0; - if (xRot <= -360.0f) - xRot = 0.0; - - if (yRot >= 360.0f) - yRot = 0.0; - if (yRot <= -360.0f) - yRot = 0.0; - - glClearColor(0.5, 0.5, 0.5, 1); - glClearDepthf(1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable(GL_DEPTH_TEST); - - //set what program to use - glUseProgram(g_programObj); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, g_textureID); - glUniform1i(g_textureLoc, 0); - - //create our perspective matrix - float mpv[16]; - float trs[16]; - float rot[16]; - - identity_matrix(mpv); - glhPerspectivef2(&mpv[0], 45.0f, (float)(width) / (float) height, 1, 10); - - translate_matrix(0, 0, -4.0, trs); - rotate_matrix(xRot, yRot, 0.0f, rot); - multiply_matrix(trs, rot, trs); - multiply_matrix(mpv, trs, mpv); - glUniformMatrix4fv(g_MVPLoc, 1, GL_FALSE, (GLfloat*)mpv); - - //define the attributes of the vertex - glBindBuffer(GL_ARRAY_BUFFER, g_vboID); - glVertexAttribPointer(g_positionLoc, - 3, - GL_FLOAT, - GL_FALSE, - sizeof(Vertex), - (void*)offsetof(Vertex, loc)); - glEnableVertexAttribArray(g_positionLoc); - glVertexAttribPointer(g_texCoordLoc, - 2, - GL_FLOAT, - GL_FALSE, - sizeof(Vertex), - (void*)offsetof(Vertex, tu)); - glEnableVertexAttribArray(g_texCoordLoc); - glVertexAttribPointer(g_colorLoc, - 3, - GL_FLOAT, - GL_FALSE, - sizeof(Vertex), - (void*)offsetof(Vertex, color)); - glEnableVertexAttribArray(g_colorLoc); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ibID); - glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0); -} - -const char* LoadData(const char* url) { - char* buf; - struct stat stat_buf; - - int fp = open(url, O_RDONLY); - fstat(fp, &stat_buf); - - int len = static_cast<int>(stat_buf.st_size); - buf = new char[len + 1]; - int read_size = read(fp, buf, len); - buf[len] = 0; - return buf; -} - -PPAPI_MAIN_USE(PPAPI_CreateInstance3D, PPAPI_MAIN_DEFAULT_ARGS) -int ppapi_main(int argc, const char* argv[]) { - printf("Started main.\n"); - - // Mount URL loads to /http - mount("", "/http", "httpfs", 0, ""); - - g_TextureData = LoadData("/http/hello.raw"); - g_VShaderData = LoadData("/http/vertex_shader_es2.vert"); - g_FShaderData = LoadData("/http/fragment_shader_es2.frag"); - g_quadVertices = BuildCube(); - - fprintf(stderr, "Loaded\n"); - g_Loaded = true; - return 0; -} diff --git a/native_client_sdk/src/examples/hello_world_instance3d/matrix.cc b/native_client_sdk/src/examples/hello_world_instance3d/matrix.cc deleted file mode 100644 index b87d496cb1..0000000000 --- a/native_client_sdk/src/examples/hello_world_instance3d/matrix.cc +++ /dev/null @@ -1,140 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/** @file matrix.cc - * Implements simple matrix manipulation functions. - */ - -//----------------------------------------------------------------------------- -#include <stdlib.h> -#include <string.h> -#include "matrix.h" -#define deg_to_rad(x) (x * (M_PI / 180.0f)) - -void glhFrustumf2(Matrix_t mat, - GLfloat left, - GLfloat right, - GLfloat bottom, - GLfloat top, - GLfloat znear, - GLfloat zfar) { - float temp, temp2, temp3, temp4; - temp = 2.0f * znear; - temp2 = right - left; - temp3 = top - bottom; - temp4 = zfar - znear; - mat[0] = temp / temp2; - mat[1] = 0.0f; - mat[2] = 0.0f; - mat[3] = 0.0f; - mat[4] = 0.0f; - mat[5] = temp / temp3; - mat[6] = 0.0f; - mat[7] = 0.0f; - mat[8] = (right + left) / temp2; - mat[9] = (top + bottom) / temp3; - mat[10] = (-zfar - znear) / temp4; - mat[11] = -1.0f; - mat[12] = 0.0f; - mat[13] = 0.0f; - mat[14] = (-temp * zfar) / temp4; - mat[15] = 0.0f; -} - -void glhPerspectivef2(Matrix_t mat, - GLfloat fovyInDegrees, - GLfloat aspectRatio, - GLfloat znear, - GLfloat zfar) { - float ymax, xmax; - ymax = znear * tanf(fovyInDegrees * 3.14f / 360.0f); - xmax = ymax * aspectRatio; - glhFrustumf2(mat, -xmax, xmax, -ymax, ymax, znear, zfar); -} - -void identity_matrix(Matrix_t mat) { - memset(mat, 0, sizeof(Matrix_t)); - mat[0] = 1.0; - mat[5] = 1.0; - mat[10] = 1.0; - mat[15] = 1.0; -} - -void multiply_matrix(const Matrix_t a, const Matrix_t b, Matrix_t mat) { - // Generate to a temporary first in case the output matrix and input - // matrix are the same. - Matrix_t out; - - out[0] = a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3]; - out[1] = a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3]; - out[2] = a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3]; - out[3] = a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3]; - - out[4] = a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7]; - out[5] = a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7]; - out[6] = a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7]; - out[7] = a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7]; - - out[8] = a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11]; - out[9] = a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11]; - out[10] = a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11]; - out[11] = a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11]; - - out[12] = a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15]; - out[13] = a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15]; - out[14] = a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15]; - out[15] = a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15]; - - memcpy(mat, out, sizeof(Matrix_t)); -} - -void rotate_x_matrix(GLfloat x_rad, Matrix_t mat) { - identity_matrix(mat); - mat[5] = cosf(x_rad); - mat[6] = -sinf(x_rad); - mat[9] = -mat[6]; - mat[10] = mat[5]; -} - -void rotate_y_matrix(GLfloat y_rad, Matrix_t mat) { - identity_matrix(mat); - mat[0] = cosf(y_rad); - mat[2] = sinf(y_rad); - mat[8] = -mat[2]; - mat[10] = mat[0]; -} - -void rotate_z_matrix(GLfloat z_rad, Matrix_t mat) { - identity_matrix(mat); - mat[0] = cosf(z_rad); - mat[1] = sinf(z_rad); - mat[4] = -mat[1]; - mat[5] = mat[0]; -} - -void rotate_matrix(GLfloat x_deg, GLfloat y_deg, GLfloat z_deg, Matrix_t mat) { - GLfloat x_rad = (GLfloat) deg_to_rad(x_deg); - GLfloat y_rad = (GLfloat) deg_to_rad(y_deg); - GLfloat z_rad = (GLfloat) deg_to_rad(z_deg); - - Matrix_t x_matrix; - Matrix_t y_matrix; - Matrix_t z_matrix; - - rotate_x_matrix(x_rad, x_matrix); - rotate_y_matrix(y_rad, y_matrix); - rotate_z_matrix(z_rad, z_matrix); - - Matrix_t xy_matrix; - multiply_matrix(y_matrix, x_matrix, xy_matrix); - multiply_matrix(z_matrix, xy_matrix, mat); -} - -void translate_matrix(GLfloat x, GLfloat y, GLfloat z, Matrix_t mat) { - identity_matrix(mat); - mat[12] += x; - mat[13] += y; - mat[14] += z; -} diff --git a/native_client_sdk/src/examples/hello_world_instance3d/matrix.h b/native_client_sdk/src/examples/hello_world_instance3d/matrix.h deleted file mode 100644 index ed094eda09..0000000000 --- a/native_client_sdk/src/examples/hello_world_instance3d/matrix.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef EXAMPLES_HELLO_WORLD_GLES_MATRIX_H -#define EXAMPLES_HELLO_WORLD_GLES_MATRIX_H - -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/** @file matrix.cc - * Implements simple matrix manipulation functions. - */ - -//----------------------------------------------------------------------------- -#define _USE_MATH_DEFINES 1 -#include <limits.h> -#include <math.h> -#include <GLES2/gl2.h> - -typedef GLfloat Matrix_t[16]; - -/// Since GLES2 doesn't have all the nifty matrix transform functions that GL -/// has, we emulate some of them here for the sake of sanity from: -/// http://www.opengl.org/wiki/GluPerspective_code -void glhFrustumf2(Matrix_t mat, - GLfloat left, - GLfloat right, - GLfloat bottom, - GLfloat top, - GLfloat znear, - GLfloat zfar); - -void glhPerspectivef2(Matrix_t mat, - GLfloat fovyInDegrees, - GLfloat aspectRatio, - GLfloat znear, - GLfloat zfar); - -void identity_matrix(Matrix_t mat); -void multiply_matrix(const Matrix_t a, const Matrix_t b, Matrix_t mat); -void rotate_matrix(GLfloat x_deg, GLfloat y_deg, GLfloat z_deg, Matrix_t mat); -void translate_matrix(GLfloat x, GLfloat y, GLfloat z, Matrix_t mat); - -#endif // EXAMPLES_HELLO_WORLD_GLES_MATRIX_H diff --git a/native_client_sdk/src/examples/hello_world_instance3d/vertex_shader_es2.vert b/native_client_sdk/src/examples/hello_world_instance3d/vertex_shader_es2.vert deleted file mode 100644 index da616cb92a..0000000000 --- a/native_client_sdk/src/examples/hello_world_instance3d/vertex_shader_es2.vert +++ /dev/null @@ -1,12 +0,0 @@ -uniform mat4 a_MVP; -attribute vec2 a_texCoord; -attribute vec3 a_color; -attribute vec4 a_position; -varying vec3 v_color; -varying vec2 v_texCoord; -void main() -{ - gl_Position = a_MVP * a_position; - v_color = a_color; - v_texCoord = a_texCoord; -}
\ No newline at end of file diff --git a/native_client_sdk/src/examples/tutorial/dlopen/dlopen.cc b/native_client_sdk/src/examples/tutorial/dlopen/dlopen.cc index 7d13a1028e..9291464165 100644 --- a/native_client_sdk/src/examples/tutorial/dlopen/dlopen.cc +++ b/native_client_sdk/src/examples/tutorial/dlopen/dlopen.cc @@ -23,9 +23,15 @@ #define CONFIG_NAME "Release" #endif -#define XSTRINGIFY(x) STRINGIFY(x) -#define STRINGIFY(x) #x -#define NACL_ARCH_STRING XSTRINGIFY(NACL_ARCH) +#if defined __arm__ +#define NACL_ARCH "arm" +#elif defined __i686__ +#define NACL_ARCH "x86_32" +#elif defined __x86_64__ +#define NACL_ARCH "x86_64" +#else +#error "Unknown arch" +#endif class DlOpenInstance : public pp::Instance { public: @@ -66,7 +72,7 @@ class DlOpenInstance : public pp::Instance { // dlclose, which would close the shared object and unload it from memory. void LoadLibrary() { const char reverse_so_path[] = - "/http/glibc/" CONFIG_NAME "/libreverse_" NACL_ARCH_STRING ".so"; + "/http/glibc/" CONFIG_NAME "/libreverse_" NACL_ARCH ".so"; const int32_t IMMEDIATELY = 0; eightball_so_ = dlopen("libeightball.so", RTLD_LAZY); reverse_so_ = dlopen(reverse_so_path, RTLD_LAZY); diff --git a/native_client_sdk/src/libraries/error_handling/error_handling.h b/native_client_sdk/src/libraries/error_handling/error_handling.h index 29b4aca96d..add76ceb41 100644 --- a/native_client_sdk/src/libraries/error_handling/error_handling.h +++ b/native_client_sdk/src/libraries/error_handling/error_handling.h @@ -8,7 +8,7 @@ #define ERROR_HANDLING_ERROR_HANDLING_H_ #include "error_handling/string_stream.h" -#include "utils/macros.h" +#include "sdk_util/macros.h" EXTERN_C_BEGIN @@ -82,4 +82,5 @@ int EHUnwindFrame(EHFrame* frame); EXTERN_C_END -#endif // ERROR_HANDLING_ERROR_HANDLING_H_
\ No newline at end of file +#endif // ERROR_HANDLING_ERROR_HANDLING_H_ + diff --git a/native_client_sdk/src/libraries/nacl_io/error.h b/native_client_sdk/src/libraries/nacl_io/error.h new file mode 100644 index 0000000000..8f7510e46e --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/error.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef LIBRARIES_NACL_IO_ERROR_H_ +#define LIBRARIES_NACL_IO_ERROR_H_ + +struct Error { + // TODO(binji): Add debugging constructor w/ __FILE__, __LINE__. + // crbug.com/247816 + Error(int error) : error(error) {} + operator int() const { return error; } + + int error; +}; + +#endif // LIBRARIES_NACL_IO_ERROR_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/inode_pool.h b/native_client_sdk/src/libraries/nacl_io/inode_pool.h index 5624b1d17a..4c3f44b36e 100644 --- a/native_client_sdk/src/libraries/nacl_io/inode_pool.h +++ b/native_client_sdk/src/libraries/nacl_io/inode_pool.h @@ -11,7 +11,7 @@ #include "nacl_io/osstat.h" #include "pthread.h" -#include "utils/auto_lock.h" +#include "sdk_util/auto_lock.h" class INodePool { diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc b/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc index dcf42fb8c6..a943894595 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc @@ -17,40 +17,57 @@ #include "nacl_io/mount_node.h" // It is only legal to construct a handle while the kernel lock is held. -KernelHandle::KernelHandle(Mount* mnt, MountNode* node, int mode) - : mount_(mnt), - node_(node), - mode_(mode), - offs_(0) { - if (mode & O_APPEND) offs_ = node->GetSize(); +KernelHandle::KernelHandle(Mount* mnt, MountNode* node) + : mount_(mnt), node_(node), offs_(0) {} + +Error KernelHandle::Init(int open_mode) { + if (open_mode & O_APPEND) { + size_t node_size; + Error error = node_->GetSize(&offs_); + if (error) + return error; + } + + return 0; } -off_t KernelHandle::Seek(off_t offset, int whence) { +Error KernelHandle::Seek(off_t offset, int whence, off_t* out_offset) { + // By default, don't move the offset. + *out_offset = offset; + size_t base; - size_t node_size = node_->GetSize(); + size_t node_size; + Error error = node_->GetSize(&node_size); + if (error) + return error; switch (whence) { - default: return -1; - case SEEK_SET: base = 0; break; - case SEEK_CUR: base = offs_; break; - case SEEK_END: base = node_size; break; + default: + return -1; + case SEEK_SET: + base = 0; + break; + case SEEK_CUR: + base = offs_; + break; + case SEEK_END: + base = node_size; + break; } - if (base + offset < 0) { - errno = EINVAL; - return -1; - } + if (base + offset < 0) + return EINVAL; - offs_ = base + offset; + off_t new_offset = base + offset; // Seeking past the end of the file will zero out the space between the old // end and the new end. - if (offs_ > node_size) { - if (node_->FTruncate(offs_) < 0) { - errno = EINVAL; - return -1; - } + if (new_offset > node_size) { + error = node_->FTruncate(new_offset); + if (error) + return EINVAL; } - return offs_; + *out_offset = offs_ = new_offset; + return 0; } diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_handle.h b/native_client_sdk/src/libraries/nacl_io/kernel_handle.h index a047125085..ab7b7cd699 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_handle.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_handle.h @@ -7,9 +7,10 @@ #include <pthread.h> +#include "nacl_io/error.h" #include "nacl_io/ostypes.h" -#include "utils/macros.h" -#include "utils/ref_object.h" +#include "sdk_util/macros.h" +#include "sdk_util/ref_object.h" class Mount; class MountNode; @@ -19,13 +20,15 @@ class MountNode; // KernelHandle can only be referenced when the KernelProxy lock is held. class KernelHandle : public RefObject { public: - KernelHandle(Mount* mnt, MountNode* node, int oflags); + // Assumes |mnt| and |node| are non-NULL. + KernelHandle(Mount* mnt, MountNode* node); - off_t Seek(off_t offset, int whence); + Error Init(int open_flags); + // Assumes |out_offset| is non-NULL. + Error Seek(off_t offset, int whence, off_t* out_offset); Mount* mount_; MountNode* node_; - int mode_; size_t offs_; private: diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h index b1bcf90d7c..c4b658dfd4 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h @@ -8,7 +8,7 @@ #include <ppapi/c/ppb.h> #include <ppapi/c/pp_instance.h> #include "nacl_io/ostypes.h" -#include "utils/macros.h" +#include "sdk_util/macros.h" EXTERN_C_BEGIN diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_object.cc b/native_client_sdk/src/libraries/nacl_io/kernel_object.cc index 5c265309cc..6be3c9779b 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_object.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_object.cc @@ -17,7 +17,7 @@ #include "nacl_io/kernel_handle.h" #include "nacl_io/mount.h" #include "nacl_io/mount_node.h" -#include "utils/auto_lock.h" +#include "sdk_util/auto_lock.h" KernelObject::KernelObject() { pthread_mutex_init(&kernel_lock_, NULL); @@ -31,8 +31,12 @@ KernelObject::~KernelObject() { // Uses longest prefix to find the mount for the give path, then // acquires the mount and returns it with a relative path. -Mount* KernelObject::AcquireMountAndPath(const std::string& relpath, - Path* out_path) { +Error KernelObject::AcquireMountAndPath(const std::string& relpath, + Mount** out_mount, + Path* out_path) { + *out_mount = NULL; + *out_path = Path(); + Path abs_path; { AutoLock lock(&process_lock_); @@ -42,7 +46,6 @@ Mount* KernelObject::AcquireMountAndPath(const std::string& relpath, AutoLock lock(&kernel_lock_); Mount* mount = NULL; - // Find longest prefix size_t max = abs_path.Size(); for (size_t len = 0; len < abs_path.Size(); len++) { @@ -55,14 +58,13 @@ Mount* KernelObject::AcquireMountAndPath(const std::string& relpath, } } - if (NULL == mount) { - errno = ENOTDIR; - return NULL; - } + if (NULL == mount) + return ENOTDIR; // Acquire the mount while we hold the proxy lock mount->Acquire(); - return mount; + *out_mount = mount; + return 0; } void KernelObject::ReleaseMount(Mount* mnt) { @@ -70,43 +72,38 @@ void KernelObject::ReleaseMount(Mount* mnt) { mnt->Release(); } -KernelHandle* KernelObject::AcquireHandle(int fd) { +Error KernelObject::AcquireHandle(int fd, KernelHandle** out_handle) { + *out_handle = NULL; + AutoLock lock(&process_lock_); - if (fd < 0 || fd >= static_cast<int>(handle_map_.size())) { - errno = EBADF; - return NULL; - } + if (fd < 0 || fd >= static_cast<int>(handle_map_.size())) + return EBADF; KernelHandle* handle = handle_map_[fd]; - if (NULL == handle) { - errno = EBADF; - return NULL; - } + if (NULL == handle) + return EBADF; // Ref count while holding parent mutex handle->Acquire(); lock.Unlock(); - if (handle->node_) handle->mount_->AcquireNode(handle->node_); + if (handle->node_) + handle->mount_->AcquireNode(handle->node_); - return handle; + *out_handle = handle; + return 0; } void KernelObject::ReleaseHandle(KernelHandle* handle) { // The handle must already be held before taking the // kernel lock. - if (handle->node_) handle->mount_->ReleaseNode(handle->node_); + if (handle->node_) + handle->mount_->ReleaseNode(handle->node_); AutoLock lock(&process_lock_); handle->Release(); } -// Helper function to properly sort FD order in the heap, forcing -// lower numbered FD to be available first. -static bool FdOrder(int i, int j) { - return i > j; -} - int KernelObject::AllocateFD(KernelHandle* handle) { AutoLock lock(&process_lock_); int id; @@ -119,7 +116,8 @@ int KernelObject::AllocateFD(KernelHandle* handle) { // If we can recycle and FD, use that first if (free_fds_.size()) { id = free_fds_.front(); - std::pop_heap(free_fds_.begin(), free_fds_.end(), FdOrder); + // Force lower numbered FD to be available first. + std::pop_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>()); free_fds_.pop_back(); handle_map_[id] = handle; } else { @@ -166,7 +164,8 @@ void KernelObject::FreeFD(int fd) { handle_map_[fd] = NULL; free_fds_.push_back(fd); - std::push_heap(free_fds_.begin(), free_fds_.end(), FdOrder); + // Force lower numbered FD to be available first. + std::push_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>()); } Path KernelObject::GetAbsPathLocked(const std::string& path) { diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_object.h b/native_client_sdk/src/libraries/nacl_io/kernel_object.h index 97b197f805..eb4c8ac041 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_object.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_object.h @@ -11,6 +11,7 @@ #include <string> #include <vector> +#include "nacl_io/error.h" #include "nacl_io/path.h" class KernelHandle; @@ -28,17 +29,25 @@ class KernelObject { KernelObject(); virtual ~KernelObject(); - // Find the mount for the given path, and acquires it - Mount* AcquireMountAndPath(const std::string& relpath, Path *pobj); + // Find the mount for the given path, and acquires it. + // Assumes |out_mount| and |out_path| are non-NULL. + Error AcquireMountAndPath(const std::string& relpath, + Mount** out_mount, + Path* out_path); + // Assumes |mnt| is non-NULL. void ReleaseMount(Mount* mnt); // Convert from FD to KernelHandle, and acquire the handle. - KernelHandle* AcquireHandle(int fd); + // Assumes |out_handle| is non-NULL. + Error AcquireHandle(int fd, KernelHandle** out_handle); + // Assumes |handle| is non-NULL. void ReleaseHandle(KernelHandle* handle); // Allocate a new fd and assign the handle to it, while // ref counting the handle and associated mount. + // Assumes |handle| is non-NULL; int AllocateFD(KernelHandle* handle); + // Assumes |handle| is non-NULL; void FreeAndReassignFD(int fd, KernelHandle* handle); void FreeFD(int fd); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc index 6d996d3ae2..2f49395ee0 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc @@ -25,8 +25,8 @@ #include "nacl_io/osstat.h" #include "nacl_io/path.h" #include "nacl_io/pepper_interface.h" -#include "utils/auto_lock.h" -#include "utils/ref_object.h" +#include "sdk_util/auto_lock.h" +#include "sdk_util/ref_object.h" #ifndef MAXPATHLEN #define MAXPATHLEN 256 @@ -36,16 +36,9 @@ #define USR_ID 1002 #define GRP_ID 1003 +KernelProxy::KernelProxy() : dev_(0), ppapi_(NULL) {} - -KernelProxy::KernelProxy() - : dev_(0), - ppapi_(NULL) { -} - -KernelProxy::~KernelProxy() { - delete ppapi_; -} +KernelProxy::~KernelProxy() { delete ppapi_; } void KernelProxy::Init(PepperInterface* ppapi) { ppapi_ = ppapi; @@ -58,11 +51,12 @@ void KernelProxy::Init(PepperInterface* ppapi) { factories_["httpfs"] = MountHttp::Create<MountHttp>; factories_["passthroughfs"] = MountPassthrough::Create<MountPassthrough>; - // Create passthrough mount at root - StringMap_t smap; - mounts_["/"] = MountPassthrough::Create<MountPassthrough>( - dev_++, smap, ppapi_); - mounts_["/dev"] = MountDev::Create<MountDev>(dev_++, smap, ppapi_); + int result; + result = mount("", "/", "passthroughfs", 0, NULL); + assert(result == 0); + + result = mount("", "/dev", "dev", 0, NULL); + assert(result == 0); // Open the first three in order to get STDIN, STDOUT, STDERR open("/dev/stdin", O_RDONLY); @@ -70,19 +64,32 @@ void KernelProxy::Init(PepperInterface* ppapi) { open("/dev/stderr", O_WRONLY); } -int KernelProxy::open(const char *path, int oflags) { +int KernelProxy::open(const char* path, int oflags) { Path rel; - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; + Mount* mnt; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } - MountNode* node = mnt->Open(rel, oflags); - if (node == NULL) { + MountNode* node = NULL; + error = mnt->Open(rel, oflags, &node); + if (error) { + errno = error; + ReleaseMount(mnt); + return -1; + } + + KernelHandle* handle = new KernelHandle(mnt, node); + error = handle->Init(oflags); + if (error) { + errno = error; ReleaseMount(mnt); return -1; } - KernelHandle* handle = new KernelHandle(mnt, node, oflags); int fd = AllocateFD(handle); mnt->AcquireNode(node); @@ -93,9 +100,12 @@ int KernelProxy::open(const char *path, int oflags) { } int KernelProxy::close(int fd) { - KernelHandle* handle = AcquireHandle(fd); - - if (NULL == handle) return -1; + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } Mount* mount = handle->mount_; // Acquire the mount to ensure FreeFD doesn't prematurely destroy it. @@ -115,8 +125,12 @@ int KernelProxy::close(int fd) { } int KernelProxy::dup(int oldfd) { - KernelHandle* handle = AcquireHandle(oldfd); - if (NULL == handle) return -1; + KernelHandle* handle; + Error error = AcquireHandle(oldfd, &handle); + if (error) { + errno = error; + return -1; + } int newfd = AllocateFD(handle); ReleaseHandle(handle); @@ -126,17 +140,21 @@ int KernelProxy::dup(int oldfd) { int KernelProxy::dup2(int oldfd, int newfd) { // If it's the same file handle, just return - if (oldfd == newfd) return newfd; + if (oldfd == newfd) + return newfd; - KernelHandle* old_handle = AcquireHandle(oldfd); - if (NULL == old_handle) return -1; + KernelHandle* old_handle; + Error error = AcquireHandle(oldfd, &old_handle); + if (error) { + errno = error; + return -1; + } FreeAndReassignFD(newfd, old_handle); ReleaseHandle(old_handle); return newfd; } - char* KernelProxy::getcwd(char* buf, size_t size) { AutoLock lock(&process_lock_); if (size <= 0) { @@ -171,42 +189,64 @@ char* KernelProxy::getwd(char* buf) { return getcwd(buf, MAXPATHLEN); } -int KernelProxy::chmod(const char *path, mode_t mode) { +int KernelProxy::chmod(const char* path, mode_t mode) { int fd = KernelProxy::open(path, O_RDWR); - if (-1 == fd) return -1; + if (-1 == fd) + return -1; - int ret = fchmod(fd, mode); + int result = fchmod(fd, mode); close(fd); - return ret; + return result; } -int KernelProxy::mkdir(const char *path, mode_t mode) { +int KernelProxy::mkdir(const char* path, mode_t mode) { + Mount* mnt; Path rel; - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } + + int result = 0; + error = mnt->Mkdir(rel, mode); + if (error) { + errno = error; + result = -1; + } - int val = mnt->Mkdir(rel, mode); ReleaseMount(mnt); - return val; + return result; } -int KernelProxy::rmdir(const char *path) { +int KernelProxy::rmdir(const char* path) { + Mount* mnt; Path rel; - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } + + int result = 0; + error = mnt->Rmdir(rel); + if (error) { + errno = error; + result = -1; + } - int val = mnt->Rmdir(rel); ReleaseMount(mnt); - return val; + return result; } -int KernelProxy::stat(const char *path, struct stat *buf) { +int KernelProxy::stat(const char* path, struct stat* buf) { int fd = open(path, O_RDONLY); - if (-1 == fd) return -1; + if (-1 == fd) + return -1; - int ret = fstat(fd, buf); + int result = fstat(fd, buf); close(fd); - return ret; + return result; } int KernelProxy::chdir(const char* path) { @@ -215,19 +255,21 @@ int KernelProxy::chdir(const char* path) { return -1; bool is_dir = (statbuf.st_mode & S_IFDIR) != 0; - if (is_dir) { - AutoLock lock(&process_lock_); - cwd_ = GetAbsPathLocked(path).Join(); - return 0; + if (!is_dir) { + errno = ENOTDIR; + return -1; } - errno = ENOTDIR; - return -1; + AutoLock lock(&process_lock_); + cwd_ = GetAbsPathLocked(path).Join(); + return 0; } -int KernelProxy::mount(const char *source, const char *target, - const char *filesystemtype, unsigned long mountflags, - const void *data) { +int KernelProxy::mount(const char* source, + const char* target, + const char* filesystemtype, + unsigned long mountflags, + const void* data) { // See if it's already mounted std::string abs_targ; @@ -255,8 +297,8 @@ int KernelProxy::mount(const char *source, const char *target, smap["TARGET"] = abs_targ; if (data) { - char* str = strdup(static_cast<const char *>(data)); - char* ptr = strtok(str,","); + char* str = strdup(static_cast<const char*>(data)); + char* ptr = strtok(str, ","); char* val; while (ptr != NULL) { val = strchr(ptr, '='); @@ -271,16 +313,18 @@ int KernelProxy::mount(const char *source, const char *target, free(str); } - Mount* mnt = factory->second(dev_++, smap, ppapi_); - if (mnt) { - mounts_[abs_targ] = mnt; - return 0; + Mount* mnt = NULL; + Error error = factory->second(dev_++, smap, ppapi_, &mnt); + if (error) { + errno = error; + return -1; } - errno = EINVAL; - return -1; + + mounts_[abs_targ] = mnt; + return 0; } -int KernelProxy::umount(const char *path) { +int KernelProxy::umount(const char* path) { Path abs_path; // Scope this lock to prevent holding both process and kernel locks @@ -307,125 +351,204 @@ int KernelProxy::umount(const char *path) { return 0; } -ssize_t KernelProxy::read(int fd, void *buf, size_t nbytes) { - KernelHandle* handle = AcquireHandle(fd); - - // check if fd is valid and handle exists - if (NULL == handle) return -1; +ssize_t KernelProxy::read(int fd, void* buf, size_t nbytes) { + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } AutoLock lock(&handle->lock_); - ssize_t cnt = handle->node_->Read(handle->offs_, buf, nbytes); - if (cnt > 0) handle->offs_ += cnt; + int cnt = 0; + error = handle->node_->Read(handle->offs_, buf, nbytes, &cnt); + if (error) + errno = error; + + if (cnt > 0) + handle->offs_ += cnt; ReleaseHandle(handle); return cnt; } -ssize_t KernelProxy::write(int fd, const void *buf, size_t nbytes) { - KernelHandle* handle = AcquireHandle(fd); - - // check if fd is valid and handle exists - if (NULL == handle) return -1; +ssize_t KernelProxy::write(int fd, const void* buf, size_t nbytes) { + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } AutoLock lock(&handle->lock_); - ssize_t cnt = handle->node_->Write(handle->offs_, buf, nbytes); - if (cnt > 0) handle->offs_ += cnt; + int cnt = 0; + error = handle->node_->Write(handle->offs_, buf, nbytes, &cnt); + if (error) + errno = error; + + if (cnt > 0) + handle->offs_ += cnt; ReleaseHandle(handle); return cnt; } int KernelProxy::fstat(int fd, struct stat* buf) { - KernelHandle* handle = AcquireHandle(fd); + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } - // check if fd is valid and handle exists - if (NULL == handle) return -1; + int result = 0; + error = handle->node_->GetStat(buf); + if (error) { + errno = error; + result = -1; + } - int ret = handle->node_->GetStat(buf); ReleaseHandle(handle); - return ret; + return result; } int KernelProxy::getdents(int fd, void* buf, unsigned int count) { - KernelHandle* handle = AcquireHandle(fd); - - // check if fd is valid and handle exists - if (NULL == handle) return -1; + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } AutoLock lock(&handle->lock_); - int cnt = handle->node_->GetDents(handle->offs_, - static_cast<dirent *>(buf), count); + int cnt = 0; + error = handle->node_ + ->GetDents(handle->offs_, static_cast<dirent*>(buf), count, &cnt); + if (error) + errno = error; - if (cnt > 0) handle->offs_ += cnt; + if (cnt > 0) + handle->offs_ += cnt; ReleaseHandle(handle); return cnt; } int KernelProxy::ftruncate(int fd, off_t length) { - KernelHandle* handle = AcquireHandle(fd); + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } - // check if fd is valid and handle exists - if (NULL == handle) return -1; - int ret = handle->node_->FTruncate(length); + int result = 0; + error = handle->node_->FTruncate(length); + if (error) { + errno = error; + result = -1; + } ReleaseHandle(handle); - return ret; + return result; } int KernelProxy::fsync(int fd) { - KernelHandle* handle = AcquireHandle(fd); + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } - // check if fd is valid and handle exists - if (NULL == handle) return -1; - int ret = handle->node_->FSync(); + int result = 0; + error = handle->node_->FSync(); + if (error) { + errno = error; + result = -1; + } ReleaseHandle(handle); - return ret; + return result; } int KernelProxy::isatty(int fd) { - KernelHandle* handle = AcquireHandle(fd); + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } - // check if fd is valid and handle exists - if (NULL == handle) return -1; - int ret = handle->node_->IsaTTY(); + int result = 0; + error = handle->node_->IsaTTY(); + if (error) { + errno = error; + result = -1; + } ReleaseHandle(handle); - return ret; + return result; } off_t KernelProxy::lseek(int fd, off_t offset, int whence) { - KernelHandle* handle = AcquireHandle(fd); - - // check if fd is valid and handle exists - if (NULL == handle) return -1; + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } AutoLock lock(&handle->lock_); - int ret = handle->Seek(offset, whence); + off_t new_offset; + error = handle->Seek(offset, whence, &new_offset); + if (error) { + errno = error; + new_offset = -1; + } ReleaseHandle(handle); - return ret; + return new_offset; } int KernelProxy::unlink(const char* path) { + Mount* mnt; Path rel; - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } + + int result = 0; + error = mnt->Unlink(rel); + if (error) { + errno = error; + result = -1; + } - int val = mnt->Unlink(rel); ReleaseMount(mnt); - return val; + return result; } int KernelProxy::remove(const char* path) { + Mount* mnt; Path rel; - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } + + int result = 0; + error = mnt->Remove(rel); + if (error) { + errno = error; + result = -1; + } - int val = mnt->Remove(rel); ReleaseMount(mnt); - return val; + return result; } // TODO(noelallen): Needs implementation. @@ -449,22 +572,29 @@ int KernelProxy::symlink(const char* oldpath, const char* newpath) { return -1; } -void* KernelProxy::mmap(void* addr, size_t length, int prot, int flags, int fd, +void* KernelProxy::mmap(void* addr, + size_t length, + int prot, + int flags, + int fd, size_t offset) { // We shouldn't be getting anonymous mmaps here. assert((flags & MAP_ANONYMOUS) == 0); assert(fd != -1); - KernelHandle* handle = AcquireHandle(fd); - - if (NULL == handle) + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; return MAP_FAILED; + } void* new_addr; { AutoLock lock(&handle->lock_); - new_addr = handle->node_->MMap(addr, length, prot, flags, offset); - if (new_addr == MAP_FAILED) { + error = handle->node_->MMap(addr, length, prot, flags, offset, &new_addr); + if (error) { + errno = error; ReleaseHandle(handle); return MAP_FAILED; } @@ -510,23 +640,34 @@ int KernelProxy::munmap(void* addr, size_t length) { } int KernelProxy::open_resource(const char* path) { + Mount* mnt; Path rel; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; - - MountNode* node = mnt->OpenResource(rel); - if (node == NULL) { - node = mnt->Open(rel, O_RDONLY); - if (node == NULL) { + MountNode* node = NULL; + error = mnt->OpenResource(rel, &node); + if (error) { + // OpenResource failed, try Open(). + error = mnt->Open(rel, O_RDONLY, &node); + if (error) { + errno = error; ReleaseMount(mnt); return -1; } } - // OpenResource failed, try Open(). + KernelHandle* handle = new KernelHandle(mnt, node); + error = handle->Init(O_RDONLY); + if (error) { + errno = error; + ReleaseMount(mnt); + return -1; + } - KernelHandle* handle = new KernelHandle(mnt, node, O_RDONLY); int fd = AllocateFD(handle); mnt->AcquireNode(node); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h index 6c7b6b31e9..e8c251a32a 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h @@ -24,9 +24,12 @@ class PepperInterface; // KernelProxy provide one-to-one mapping for libc kernel calls. Calls to the // proxy will result in IO access to the provided Mount and MountNode objects. +// +// NOTE: The KernelProxy is the only class that should be setting errno. All +// other classes should return Error (as defined by nacl_io/error.h). class KernelProxy : protected KernelObject { public: - typedef Mount* (*MountFactory_t)(int, StringMap_t&, PepperInterface*); + typedef Error (*MountFactory_t)(int, StringMap_t&, PepperInterface*, Mount**); typedef std::map<std::string, std::string> StringMap_t; typedef std::map<std::string, MountFactory_t> MountFactoryMap_t; diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h b/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h index 87dca82eea..322fe72154 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h @@ -7,7 +7,7 @@ #include <sys/types.h> #include <stdlib.h> -#include "utils/macros.h" +#include "sdk_util/macros.h" #if defined(__GLIBC__) #include <sys/cdefs.h> diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_real.h b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_real.h index 7d7dd418c0..c4ff5e0e94 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_real.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_real.h @@ -6,7 +6,7 @@ #define LIBRARIES_NACL_IO_KERNEL_WRAP_REAL_H_ #include "nacl_io/ostypes.h" -#include "utils/macros.h" +#include "sdk_util/macros.h" EXTERN_C_BEGIN diff --git a/native_client_sdk/src/libraries/nacl_io/library.dsc b/native_client_sdk/src/libraries/nacl_io/library.dsc index 2954618a57..81d621cfa2 100644 --- a/native_client_sdk/src/libraries/nacl_io/library.dsc +++ b/native_client_sdk/src/libraries/nacl_io/library.dsc @@ -2,8 +2,7 @@ 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win'], 'SEARCH': [ '.', - 'pepper', - '../utils' + 'pepper' ], 'TARGETS': [ { @@ -25,6 +24,7 @@ "mount_node.cc", "mount_node_dir.cc", "mount_node_html5fs.cc", + "mount_node_http.cc", "mount_node_mem.cc", "mount_passthrough.cc", "nacl_io.cc", @@ -37,6 +37,7 @@ 'HEADERS': [ { 'FILES': [ + "error.h", "inode_pool.h", "kernel_handle.h", "kernel_intercept.h", @@ -52,6 +53,7 @@ "mount_node_dir.h", "mount_node.h", "mount_node_html5fs.h", + "mount_node_http.h", "mount_node_mem.h", "mount_passthrough.h", "nacl_io.h", @@ -73,14 +75,6 @@ "undef_macros.h", ], 'DEST': 'include/nacl_io/pepper', - }, - { - 'FILES': [ - "auto_lock.h", - "macros.h", - "ref_object.h" - ], - 'DEST': 'include/utils', } ], 'DEST': 'src', diff --git a/native_client_sdk/src/libraries/nacl_io/mount.cc b/native_client_sdk/src/libraries/nacl_io/mount.cc index 8b583db6fa..76fe3a7848 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount.cc @@ -13,23 +13,21 @@ #include "nacl_io/mount_node_mem.h" #include "nacl_io/osstat.h" #include "nacl_io/path.h" -#include "utils/auto_lock.h" -#include "utils/ref_object.h" +#include "sdk_util/auto_lock.h" +#include "sdk_util/ref_object.h" #if defined(WIN32) #include <windows.h> #endif -Mount::Mount() - : dev_(0) { -} +Mount::Mount() : dev_(0) {} Mount::~Mount() {} -bool Mount::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { +Error Mount::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { dev_ = dev; ppapi_ = ppapi; - return true; + return 0; } void Mount::Destroy() {} @@ -44,22 +42,30 @@ void Mount::ReleaseNode(MountNode* node) { node->Release(); } +Error Mount::OpenResource(const Path& path, MountNode** out_node) { + *out_node = NULL; + return EINVAL; +} + int Mount::OpenModeToPermission(int mode) { int out; switch (mode & 3) { - case O_RDONLY: out = S_IREAD; - case O_WRONLY: out = S_IWRITE; - case O_RDWR: out = S_IREAD | S_IWRITE; + case O_RDONLY: + out = S_IREAD; + case O_WRONLY: + out = S_IWRITE; + case O_RDWR: + out = S_IREAD | S_IWRITE; } return out; } - void Mount::OnNodeCreated(MountNode* node) { node->stat_.st_ino = inode_pool_.Acquire(); node->stat_.st_dev = dev_; } void Mount::OnNodeDestroyed(MountNode* node) { - if (node->stat_.st_ino) inode_pool_.Release(node->stat_.st_ino); -}
\ No newline at end of file + if (node->stat_.st_ino) + inode_pool_.Release(node->stat_.st_ino); +} diff --git a/native_client_sdk/src/libraries/nacl_io/mount.h b/native_client_sdk/src/libraries/nacl_io/mount.h index 622dae0207..ddf1fd5c76 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount.h +++ b/native_client_sdk/src/libraries/nacl_io/mount.h @@ -8,18 +8,20 @@ #include <map> #include <string> +#include "nacl_io/error.h" #include "nacl_io/inode_pool.h" #include "nacl_io/mount_node.h" #include "nacl_io/path.h" -#include "utils/macros.h" -#include "utils/ref_object.h" +#include "sdk_util/macros.h" +#include "sdk_util/ref_object.h" class MountNode; class PepperInterface; typedef std::map<std::string, std::string> StringMap_t; - +// NOTE: The KernelProxy is the only class that should be setting errno. All +// other classes should return Error (as defined by nacl_io/error.h). class Mount : public RefObject { protected: // The protected functions are only used internally and will not @@ -30,38 +32,49 @@ class Mount : public RefObject { // Init must be called by the factory before the mount is used. // This function must assign a root node, or replace FindNode. // |ppapi| can be NULL. If so, this mount cannot make any pepper calls. - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); public: template <class M> - static Mount* Create(int dev, StringMap_t& args, PepperInterface* ppapi); + // Assumes that |out_mount| is non-NULL. + static Error Create(int dev, + StringMap_t& args, + PepperInterface* ppapi, + Mount** out_mount); PepperInterface* ppapi() { return ppapi_; } - // All paths are expected to containing a leading "/" + // Assumes that |node| is non-NULL. void AcquireNode(MountNode* node); + // Assumes that |node| is non-NULL. void ReleaseNode(MountNode* node); + // All paths in functions below are expected to containing a leading "/". + // Open a node at |path| with the specified open flags. The resulting // MountNode is created with a ref count of 1. - virtual MountNode *Open(const Path& path, int o_flags) = 0; + // Assumes that |out_node| is non-NULL. + virtual Error Open(const Path& path, int o_flags, MountNode** out_node) = 0; // OpenResource is only used to read files from the NaCl NMF file. No mount // except MountPassthrough should implement it. - virtual MountNode *OpenResource(const Path& path) { return NULL; } + // Assumes that |out_node| is non-NULL. + virtual Error OpenResource(const Path& path, MountNode** out_node); // Unlink, Mkdir, Rmdir will affect the both the RefCount // and the nlink number in the stat object. - virtual int Unlink(const Path& path) = 0; - virtual int Mkdir(const Path& path, int permissions) = 0; - virtual int Rmdir(const Path& path) = 0; - virtual int Remove(const Path& path) = 0; + virtual Error Unlink(const Path& path) = 0; + virtual Error Mkdir(const Path& path, int permissions) = 0; + virtual Error Rmdir(const Path& path) = 0; + virtual Error Remove(const Path& path) = 0; // Convert from R,W,R/W open flags to STAT permission flags static int OpenModeToPermission(int mode); - void OnNodeCreated(MountNode* node) ; + // Assumes that |node| is non-NULL. + void OnNodeCreated(MountNode* node); + // Assumes that |node| is non-NULL. void OnNodeDestroyed(MountNode* node); protected: @@ -81,16 +94,22 @@ class Mount : public RefObject { DISALLOW_COPY_AND_ASSIGN(Mount); }; - -template <class M> /*static*/ -Mount* Mount::Create(int dev, StringMap_t& args, PepperInterface* ppapi) { +template <class M> +Error Mount::Create(int dev, + StringMap_t& args, + PepperInterface* ppapi, + Mount** out_mount) { Mount* mnt = new M(); - if (mnt->Init(dev, args, ppapi) == false) { + Error error = mnt->Init(dev, args, ppapi); + if (error) { delete mnt; - return NULL; + *out_mount = NULL; + return error; } - return mnt; + + *out_mount = mnt; + return 0; } #endif // LIBRARIES_NACL_IO_MOUNT_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc index 83d2e55423..11893ea09e 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc @@ -14,31 +14,26 @@ #include "nacl_io/mount_node.h" #include "nacl_io/mount_node_dir.h" #include "nacl_io/pepper_interface.h" -#include "utils/auto_lock.h" +#include "sdk_util/auto_lock.h" #if defined(__native_client__) -# include <irt.h> +#include <irt.h> #elif defined(WIN32) -# include <stdlib.h> +#include <stdlib.h> #endif - namespace { -void ReleaseAndNullNode(MountNode** node) { - if (*node) - (*node)->Release(); - *node = NULL; -} - - class RealNode : public MountNode { public: RealNode(Mount* mount, int fd); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); - virtual int GetStat(struct stat* stat); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + virtual Error GetStat(struct stat* stat); protected: int fd_; @@ -48,43 +43,56 @@ class NullNode : public MountNode { public: explicit NullNode(Mount* mount); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); }; class ConsoleNode : public NullNode { public: ConsoleNode(Mount* mount, PP_LogLevel level); - virtual int Write(size_t offs, const void* buf, size_t count); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); -private: + private: PP_LogLevel level_; }; - class TtyNode : public NullNode { public: explicit TtyNode(Mount* mount); - virtual int Write(size_t offs, const void* buf, size_t count); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); }; - class ZeroNode : public MountNode { public: explicit ZeroNode(Mount* mount); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); }; class UrandomNode : public MountNode { public: explicit UrandomNode(Mount* mount); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); private: #if defined(__native_client__) @@ -93,132 +101,141 @@ class UrandomNode : public MountNode { #endif }; -RealNode::RealNode(Mount* mount, int fd) - : MountNode(mount), - fd_(fd) { +RealNode::RealNode(Mount* mount, int fd) : MountNode(mount), fd_(fd) { stat_.st_mode = S_IFCHR; } -int RealNode::Read(size_t offs, void* buf, size_t count) { +Error RealNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; + size_t readcnt; int err = _real_read(fd_, buf, count, &readcnt); - if (err) { - errno = err; - return -1; - } - return static_cast<int>(readcnt); + if (err) + return err; + + *out_bytes = static_cast<int>(readcnt); + return 0; } -int RealNode::Write(size_t offs, const void* buf, size_t count) { +Error RealNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + size_t writecnt; int err = _real_write(fd_, buf, count, &writecnt); - if (err) { - errno = err; - return -1; - } - return static_cast<int>(writecnt); -} + if (err) + return err; -int RealNode::GetStat(struct stat* stat) { - int err = _real_fstat(fd_, stat); - if (err) { - errno = err; - return -1; - } + *out_bytes = static_cast<int>(writecnt); return 0; } -NullNode::NullNode(Mount* mount) - : MountNode(mount) { - stat_.st_mode = S_IFCHR; -} +Error RealNode::GetStat(struct stat* stat) { return _real_fstat(fd_, stat); } -int NullNode::Read(size_t offs, void* buf, size_t count) { +NullNode::NullNode(Mount* mount) : MountNode(mount) { stat_.st_mode = S_IFCHR; } + +Error NullNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; return 0; } -int NullNode::Write(size_t offs, const void* buf, size_t count) { - return count; +Error NullNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = count; + return 0; } ConsoleNode::ConsoleNode(Mount* mount, PP_LogLevel level) - : NullNode(mount), - level_(level) { + : NullNode(mount), level_(level) { stat_.st_mode = S_IFCHR; } -int ConsoleNode::Write(size_t offs, const void* buf, size_t count) { +Error ConsoleNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + ConsoleInterface* con_intr = mount_->ppapi()->GetConsoleInterface(); VarInterface* var_intr = mount_->ppapi()->GetVarInterface(); - if (var_intr && con_intr) { - const char* data = static_cast<const char *>(buf); - uint32_t len = static_cast<uint32_t>(count); - struct PP_Var val = var_intr->VarFromUtf8(data, len); - con_intr->Log(mount_->ppapi()->GetInstance(), level_, val); - return count; - } + if (!(var_intr && con_intr)) + return ENOSYS; + + const char* data = static_cast<const char*>(buf); + uint32_t len = static_cast<uint32_t>(count); + struct PP_Var val = var_intr->VarFromUtf8(data, len); + con_intr->Log(mount_->ppapi()->GetInstance(), level_, val); + + *out_bytes = count; return 0; } +TtyNode::TtyNode(Mount* mount) : NullNode(mount) {} -TtyNode::TtyNode(Mount* mount) - : NullNode(mount) { -} +Error TtyNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; -int TtyNode::Write(size_t offs, const void* buf, size_t count) { MessagingInterface* msg_intr = mount_->ppapi()->GetMessagingInterface(); VarInterface* var_intr = mount_->ppapi()->GetVarInterface(); - if (var_intr && msg_intr) { - const char* data = static_cast<const char *>(buf); - uint32_t len = static_cast<uint32_t>(count); - struct PP_Var val = var_intr->VarFromUtf8(data, len); - msg_intr->PostMessage(mount_->ppapi()->GetInstance(), val); - return count; - } - return 0; -} + if (!(var_intr && msg_intr)) + return ENOSYS; + const char* data = static_cast<const char*>(buf); + uint32_t len = static_cast<uint32_t>(count); + struct PP_Var val = var_intr->VarFromUtf8(data, len); + msg_intr->PostMessage(mount_->ppapi()->GetInstance(), val); -ZeroNode::ZeroNode(Mount* mount) - : MountNode(mount) { - stat_.st_mode = S_IFCHR; + *out_bytes = count; + return 0; } -int ZeroNode::Read(size_t offs, void* buf, size_t count) { +ZeroNode::ZeroNode(Mount* mount) : MountNode(mount) { stat_.st_mode = S_IFCHR; } + +Error ZeroNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { memset(buf, 0, count); - return count; + *out_bytes = count; + return 0; } -int ZeroNode::Write(size_t offs, const void* buf, size_t count) { - return count; +Error ZeroNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = count; + return 0; } -UrandomNode::UrandomNode(Mount* mount) - : MountNode(mount) { +UrandomNode::UrandomNode(Mount* mount) : MountNode(mount) { stat_.st_mode = S_IFCHR; #if defined(__native_client__) - size_t result = nacl_interface_query(NACL_IRT_RANDOM_v0_1, &random_interface_, - sizeof(random_interface_)); + size_t result = nacl_interface_query( + NACL_IRT_RANDOM_v0_1, &random_interface_, sizeof(random_interface_)); interface_ok_ = result != 0; #endif } -int UrandomNode::Read(size_t offs, void* buf, size_t count) { +Error UrandomNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; + #if defined(__native_client__) - if (interface_ok_) { - size_t nread; - int result = (*random_interface_.get_random_bytes)(buf, count, &nread); - if (result != 0) { - errno = result; - return 0; - } + if (!interface_ok_) + return EBADF; - return count; - } + size_t nread; + int error = (*random_interface_.get_random_bytes)(buf, count, &nread); + if (error) + return error; - errno = EBADF; + *out_bytes = count; return 0; #elif defined(WIN32) char* out = static_cast<char*>(buf); @@ -227,8 +244,8 @@ int UrandomNode::Read(size_t offs, void* buf, size_t count) { unsigned int random_int; errno_t err = rand_s(&random_int); if (err) { - errno = err; - return count - bytes_left; + *out_bytes = count - bytes_left; + return err; } int bytes_to_copy = std::min(bytes_left, sizeof(random_int)); @@ -237,107 +254,84 @@ int UrandomNode::Read(size_t offs, void* buf, size_t count) { bytes_left -= bytes_to_copy; } - return count; + *out_bytes = count; + return 0; #endif } -int UrandomNode::Write(size_t offs, const void* buf, size_t count) { - return count; +Error UrandomNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = count; + return 0; } - - } // namespace -MountNode *MountDev::Open(const Path& path, int mode) { +Error MountDev::Open(const Path& path, int mode, MountNode** out_node) { + *out_node = NULL; + AutoLock lock(&lock_); // Don't allow creating any files. if (mode & O_CREAT) - return NULL; + return EINVAL; - MountNode* node = root_->FindChild(path.Join()); - if (node) - node->Acquire(); - return node; -} + MountNode* node = NULL; + int error = root_->FindChild(path.Join(), &node); + if (error) + return error; -int MountDev::Unlink(const Path& path) { - errno = EINVAL; - return -1; + node->Acquire(); + *out_node = node; + return 0; } -int MountDev::Mkdir(const Path& path, int permissions) { - errno = EINVAL; - return -1; -} +Error MountDev::Unlink(const Path& path) { return EINVAL; } -int MountDev::Rmdir(const Path& path) { - errno = EINVAL; - return -1; -} +Error MountDev::Mkdir(const Path& path, int permissions) { return EINVAL; } -int MountDev::Remove(const Path& path) { - errno = EINVAL; - return -1; -} +Error MountDev::Rmdir(const Path& path) { return EINVAL; } -MountDev::MountDev() - : null_node_(NULL), - zero_node_(NULL), - random_node_(NULL), - console0_node_(NULL), - console1_node_(NULL), - console2_node_(NULL), - console3_node_(NULL), - tty_node_(NULL) { -} +Error MountDev::Remove(const Path& path) { return EINVAL; } -bool MountDev::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { - if (!Mount::Init(dev, args, ppapi)) - return false; +MountDev::MountDev() {} + +#define INITIALIZE_DEV_NODE(path, klass) \ + error = root_->AddChild(path, new klass(this)); \ + if (error) \ + return error; + +#define INITIALIZE_DEV_NODE_1(path, klass, arg) \ + error = root_->AddChild(path, new klass(this, arg)); \ + if (error) \ + return error; + +Error MountDev::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { + Error error = Mount::Init(dev, args, ppapi); + if (error) + return error; root_ = new MountNodeDir(this); - null_node_ = new NullNode(this); - root_->AddChild("/null", null_node_); - zero_node_ = new ZeroNode(this); - root_->AddChild("/zero", zero_node_); - random_node_ = new UrandomNode(this); - root_->AddChild("/urandom", random_node_); - - console0_node_ = new ConsoleNode(this, PP_LOGLEVEL_TIP); - root_->AddChild("/console0", console0_node_); - console1_node_ = new ConsoleNode(this, PP_LOGLEVEL_LOG); - root_->AddChild("/console1", console1_node_); - console2_node_ = new ConsoleNode(this, PP_LOGLEVEL_WARNING); - root_->AddChild("/console2", console2_node_); - console3_node_ = new ConsoleNode(this, PP_LOGLEVEL_ERROR); - root_->AddChild("/console3", console3_node_); - - tty_node_ = new TtyNode(this); - root_->AddChild("/tty", tty_node_); - - stdin_node_ = new RealNode(this, 0); - root_->AddChild("/stdin", stdin_node_); - stdout_node_ = new RealNode(this, 1); - root_->AddChild("/stdout", stdout_node_); - stderr_node_ = new RealNode(this, 2); - root_->AddChild("/stderr", stderr_node_); - - return true; + + INITIALIZE_DEV_NODE("/null", NullNode); + INITIALIZE_DEV_NODE("/zero", ZeroNode); + INITIALIZE_DEV_NODE("/urandom", UrandomNode); + INITIALIZE_DEV_NODE_1("/console0", ConsoleNode, PP_LOGLEVEL_TIP); + INITIALIZE_DEV_NODE_1("/console1", ConsoleNode, PP_LOGLEVEL_LOG); + INITIALIZE_DEV_NODE_1("/console2", ConsoleNode, PP_LOGLEVEL_WARNING); + INITIALIZE_DEV_NODE_1("/console3", ConsoleNode, PP_LOGLEVEL_ERROR); + INITIALIZE_DEV_NODE("/tty", TtyNode); + INITIALIZE_DEV_NODE_1("/stdin", RealNode, 0); + INITIALIZE_DEV_NODE_1("/stdout", RealNode, 1); + INITIALIZE_DEV_NODE_1("/stderr", RealNode, 2); + + return 0; } void MountDev::Destroy() { - ReleaseAndNullNode(&stdin_node_); - ReleaseAndNullNode(&stdout_node_); - ReleaseAndNullNode(&stderr_node_); - ReleaseAndNullNode(&tty_node_); - ReleaseAndNullNode(&console3_node_); - ReleaseAndNullNode(&console2_node_); - ReleaseAndNullNode(&console1_node_); - ReleaseAndNullNode(&console0_node_); - ReleaseAndNullNode(&random_node_); - ReleaseAndNullNode(&zero_node_); - ReleaseAndNullNode(&null_node_); - ReleaseAndNullNode(&root_); + if (root_) + root_->Release(); + root_ = NULL; } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_dev.h b/native_client_sdk/src/libraries/nacl_io/mount_dev.h index ff3d99c124..7ad7daa3b9 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_dev.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.h @@ -11,32 +11,21 @@ class MountNode; class MountDev : public Mount { public: - virtual MountNode *Open(const Path& path, int mode); + virtual Error Open(const Path& path, int mode, MountNode** out_node); - virtual int Unlink(const Path& path); - virtual int Mkdir(const Path& path, int permissions); - virtual int Rmdir(const Path& path); - virtual int Remove(const Path& path); + virtual Error Unlink(const Path& path); + virtual Error Mkdir(const Path& path, int permissions); + virtual Error Rmdir(const Path& path); + virtual Error Remove(const Path& path); protected: MountDev(); - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); private: MountNode* root_; - MountNode* null_node_; - MountNode* zero_node_; - MountNode* random_node_; - MountNode* console0_node_; - MountNode* console1_node_; - MountNode* console2_node_; - MountNode* console3_node_; - MountNode* tty_node_; - MountNode* stderr_node_; - MountNode* stdin_node_; - MountNode* stdout_node_; friend class Mount; }; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc index 9b995359ae..0008284253 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc @@ -12,7 +12,7 @@ #include <string.h> #include <algorithm> #include "nacl_io/mount_node_html5fs.h" -#include "utils/auto_lock.h" +#include "sdk_util/auto_lock.h" namespace { @@ -24,96 +24,85 @@ int64_t strtoull(const char* nptr, char** endptr, int base) { } // namespace -MountNode *MountHtml5Fs::Open(const Path& path, int mode) { - if (BlockUntilFilesystemOpen() != PP_OK) { - errno = ENODEV; - return NULL; - } +Error MountHtml5Fs::Open(const Path& path, int mode, MountNode** out_node) { + *out_node = NULL; + + Error error = BlockUntilFilesystemOpen(); + if (error) + return error; - PP_Resource fileref = ppapi()->GetFileRefInterface()->Create( - filesystem_resource_, path.Join().c_str()); + PP_Resource fileref = ppapi()->GetFileRefInterface() + ->Create(filesystem_resource_, path.Join().c_str()); if (!fileref) - return NULL; + return ENOSYS; MountNodeHtml5Fs* node = new MountNodeHtml5Fs(this, fileref); - if (!node->Init(mode)) { + error = node->Init(mode); + if (error) { node->Release(); - return NULL; + return error; } - return node; + *out_node = node; + return 0; } -int MountHtml5Fs::Unlink(const Path& path) { - return Remove(path); -} +Error MountHtml5Fs::Unlink(const Path& path) { return Remove(path); } -int MountHtml5Fs::Mkdir(const Path& path, int permissions) { - if (BlockUntilFilesystemOpen() != PP_OK) { - errno = ENODEV; - return -1; - } +Error MountHtml5Fs::Mkdir(const Path& path, int permissions) { + Error error = BlockUntilFilesystemOpen(); + if (error) + return error; ScopedResource fileref_resource( - ppapi(), ppapi()->GetFileRefInterface()->Create(filesystem_resource_, - path.Join().c_str())); - if (!fileref_resource.pp_resource()) { - errno = EINVAL; - return -1; - } + ppapi(), + ppapi()->GetFileRefInterface()->Create(filesystem_resource_, + path.Join().c_str())); + if (!fileref_resource.pp_resource()) + return EIO; int32_t result = ppapi()->GetFileRefInterface()->MakeDirectory( fileref_resource.pp_resource(), PP_FALSE, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } + if (result != PP_OK) + return PPErrorToErrno(result); return 0; } -int MountHtml5Fs::Rmdir(const Path& path) { - return Remove(path); -} +Error MountHtml5Fs::Rmdir(const Path& path) { return Remove(path); } -int MountHtml5Fs::Remove(const Path& path) { - if (BlockUntilFilesystemOpen() != PP_OK) { - errno = ENODEV; - return -1; - } +Error MountHtml5Fs::Remove(const Path& path) { + Error error = BlockUntilFilesystemOpen(); + if (error) + return error; ScopedResource fileref_resource( - ppapi(), ppapi()->GetFileRefInterface()->Create(filesystem_resource_, - path.Join().c_str())); - if (!fileref_resource.pp_resource()) { - errno = EINVAL; - return -1; - } + ppapi(), + ppapi()->GetFileRefInterface()->Create(filesystem_resource_, + path.Join().c_str())); + if (!fileref_resource.pp_resource()) + return ENOSYS; - int32_t result = ppapi()->GetFileRefInterface()->Delete( - fileref_resource.pp_resource(), - PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } + int32_t result = ppapi()->GetFileRefInterface() + ->Delete(fileref_resource.pp_resource(), PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); return 0; } - MountHtml5Fs::MountHtml5Fs() : filesystem_resource_(0), filesystem_open_has_result_(false), - filesystem_open_result_(PP_OK) { -} + filesystem_open_error_(0) {} -bool MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { - if (!Mount::Init(dev, args, ppapi)) - return false; +Error MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { + Error error = Mount::Init(dev, args, ppapi); + if (error) + return error; if (!ppapi) - return false; + return ENOSYS; pthread_cond_init(&filesystem_open_cond_, NULL); @@ -121,7 +110,7 @@ bool MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { PP_FileSystemType filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT; int64_t expected_size = 0; for (StringMap_t::iterator iter = args.begin(), end = args.end(); iter != end; - ++iter) { + ++iter) { if (iter->first == "type") { if (iter->second == "PERSISTENT") { filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT; @@ -134,33 +123,32 @@ bool MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { } // Initialize filesystem. - filesystem_resource_ = ppapi->GetFileSystemInterface()->Create( - ppapi_->GetInstance(), filesystem_type); - + filesystem_resource_ = ppapi->GetFileSystemInterface() + ->Create(ppapi_->GetInstance(), filesystem_type); if (filesystem_resource_ == 0) - return false; + return ENOSYS; // We can't block the main thread, so make an asynchronous call if on main // thread. If we are off-main-thread, then don't make an asynchronous call; // otherwise we require a message loop. bool main_thread = ppapi->IsMainThread(); - PP_CompletionCallback cc = main_thread ? - PP_MakeCompletionCallback(&MountHtml5Fs::FilesystemOpenCallbackThunk, - this) : - PP_BlockUntilComplete(); + PP_CompletionCallback cc = + main_thread ? PP_MakeCompletionCallback( + &MountHtml5Fs::FilesystemOpenCallbackThunk, this) + : PP_BlockUntilComplete(); - int32_t result = ppapi->GetFileSystemInterface()->Open( - filesystem_resource_, expected_size, cc); + int32_t result = ppapi->GetFileSystemInterface() + ->Open(filesystem_resource_, expected_size, cc); if (!main_thread) { filesystem_open_has_result_ = true; - filesystem_open_result_ = result; + filesystem_open_error_ = PPErrorToErrno(result); - return filesystem_open_result_ == PP_OK; + return filesystem_open_error_; } else { // We have to assume the call to Open will succeed; there is no better // result to return here. - return true; + return 0; } } @@ -169,12 +157,12 @@ void MountHtml5Fs::Destroy() { pthread_cond_destroy(&filesystem_open_cond_); } -int32_t MountHtml5Fs::BlockUntilFilesystemOpen() { +Error MountHtml5Fs::BlockUntilFilesystemOpen() { AutoLock lock(&lock_); while (!filesystem_open_has_result_) { pthread_cond_wait(&filesystem_open_cond_, &lock_); } - return filesystem_open_result_; + return filesystem_open_error_; } // static @@ -187,6 +175,6 @@ void MountHtml5Fs::FilesystemOpenCallbackThunk(void* user_data, void MountHtml5Fs::FilesystemOpenCallback(int32_t result) { AutoLock lock(&lock_); filesystem_open_has_result_ = true; - filesystem_open_result_ = result; + filesystem_open_error_ = PPErrorToErrno(result); pthread_cond_signal(&filesystem_open_cond_); } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h index a6633be8af..e872dae353 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h @@ -11,23 +11,23 @@ class MountNode; -class MountHtml5Fs: public Mount { +class MountHtml5Fs : public Mount { public: - virtual MountNode *Open(const Path& path, int mode); - virtual int Unlink(const Path& path); - virtual int Mkdir(const Path& path, int permissions); - virtual int Rmdir(const Path& path); - virtual int Remove(const Path& path); + virtual Error Open(const Path& path, int mode, MountNode** out_node); + virtual Error Unlink(const Path& path); + virtual Error Mkdir(const Path& path, int permissions); + virtual Error Rmdir(const Path& path); + virtual Error Remove(const Path& path); PP_Resource filesystem_resource() { return filesystem_resource_; } protected: MountHtml5Fs(); - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); - int32_t BlockUntilFilesystemOpen(); + Error BlockUntilFilesystemOpen(); private: static void FilesystemOpenCallbackThunk(void* user_data, int32_t result); @@ -35,7 +35,7 @@ class MountHtml5Fs: public Mount { PP_Resource filesystem_resource_; bool filesystem_open_has_result_; // protected by lock_. - int32_t filesystem_open_result_; // protected by lock_. + Error filesystem_open_error_; // protected by lock_. pthread_cond_t filesystem_open_cond_; friend class Mount; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_http.cc index 9a6579ed07..7b4316e842 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_http.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_http.cc @@ -4,30 +4,30 @@ */ #include "nacl_io/mount_http.h" + #include <assert.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> -#include <ppapi/c/pp_errors.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> + #include <vector> -#include "nacl_io/mount_node_dir.h" -#include "nacl_io/osinttypes.h" -#include "utils/auto_lock.h" -#if defined(WIN32) -#define snprintf _snprintf -#endif +#include <ppapi/c/pp_errors.h> +#include "nacl_io/mount_node_dir.h" +#include "nacl_io/mount_node_http.h" +#include "nacl_io/osinttypes.h" +#include "sdk_util/auto_lock.h" namespace { -typedef std::vector<char *> StringList_t; -size_t SplitString(char *str, const char *delim, StringList_t* list) { - char *item = strtok(str, delim); +typedef std::vector<char*> StringList_t; +size_t SplitString(char* str, const char* delim, StringList_t* list) { + char* item = strtok(str, delim); list->clear(); while (item) { @@ -38,13 +38,7 @@ size_t SplitString(char *str, const char *delim, StringList_t* list) { return list->size(); } - -// If we're attempting to read a partial request, but the server returns a full -// request, we need to read all of the data up to the start of our partial -// request into a dummy buffer. This is the maximum size of that buffer. -const size_t MAX_READ_BUFFER_SIZE = 64 * 1024; -const int32_t STATUSCODE_OK = 200; -const int32_t STATUSCODE_PARTIAL_CONTENT = 206; +} // namespace std::string NormalizeHeaderKey(const std::string& s) { // Capitalize the first letter and any letter following a hyphen: @@ -60,557 +54,64 @@ std::string NormalizeHeaderKey(const std::string& s) { return result; } -StringMap_t ParseHeaders(const char* headers, int32_t headers_length) { - enum State { - FINDING_KEY, - SKIPPING_WHITESPACE, - FINDING_VALUE, - }; - - StringMap_t result; - std::string key; - std::string value; - - State state = FINDING_KEY; - const char* start = headers; - for (int i = 0; i < headers_length; ++i) { - switch (state) { - case FINDING_KEY: - if (headers[i] == ':') { - // Found key. - key.assign(start, &headers[i] - start); - key = NormalizeHeaderKey(key); - state = SKIPPING_WHITESPACE; - } - break; - - case SKIPPING_WHITESPACE: - if (headers[i] == ' ') { - // Found whitespace, keep going... - break; - } - - // Found a non-whitespace, mark this as the start of the value. - start = &headers[i]; - state = FINDING_VALUE; - // Fallthrough to start processing value without incrementing i. - - case FINDING_VALUE: - if (headers[i] == '\n') { - // Found value. - value.assign(start, &headers[i] - start); - result[key] = value; - start = &headers[i + 1]; - state = FINDING_KEY; - } - break; - } - } - - return result; -} - -bool ParseContentLength(const StringMap_t& headers, size_t* content_length) { - StringMap_t::const_iterator iter = headers.find("Content-Length"); - if (iter == headers.end()) - return false; - - *content_length = strtoul(iter->second.c_str(), NULL, 10); - return true; -} - -bool ParseContentRange(const StringMap_t& headers, size_t* read_start, - size_t* read_end, size_t* entity_length) { - StringMap_t::const_iterator iter = headers.find("Content-Range"); - if (iter == headers.end()) - return false; - - // The key should look like "bytes ##-##/##" or "bytes ##-##/*". The last - // value is the entity length, which can potentially be * (i.e. unknown). - int read_start_int; - int read_end_int; - int entity_length_int; - int result = sscanf(iter->second.c_str(), "bytes %"SCNuS"-%"SCNuS"/%"SCNuS, - &read_start_int, &read_end_int, &entity_length_int); - - // The Content-Range header specifies an inclusive range: e.g. the first ten - // bytes is "bytes 0-9/*". Convert it to a half-open range by incrementing - // read_end. - if (result == 2) { - *read_start = read_start_int; - *read_end = read_end_int + 1; - *entity_length = 0; - return true; - } else if (result == 3) { - *read_start = read_start_int; - *read_end = read_end_int + 1; - *entity_length = entity_length_int; - return true; - } - - return false; -} - -} // namespace - - -class MountNodeHttp : public MountNode { - public: - virtual int FSync(); - virtual int GetDents(size_t offs, struct dirent* pdir, size_t count); - virtual int GetStat(struct stat* stat); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int FTruncate(off_t size); - virtual int Write(size_t offs, const void* buf, size_t count); - virtual size_t GetSize(); - - void SetCachedSize(off_t size); - - protected: - MountNodeHttp(Mount* mount, const std::string& url, bool cache_content); - - private: - bool OpenUrl(const char* method, - StringMap_t* request_headers, - PP_Resource* out_loader, - PP_Resource* out_request, - PP_Resource* out_response, - int32_t* out_statuscode, - StringMap_t* out_response_headers); - - int DownloadToCache(); - int ReadPartialFromCache(size_t offs, void* buf, size_t count); - int DownloadPartial(size_t offs, void* buf, size_t count); - int DownloadToBuffer(PP_Resource loader, void* buf, size_t count); - - std::string url_; - std::vector<char> buffer_; - - bool cache_content_; - bool has_cached_size_; - std::vector<char> cached_data_; - - friend class MountHttp; -}; - -void MountNodeHttp::SetCachedSize(off_t size) { - has_cached_size_ = true; - stat_.st_size = size; -} - -int MountNodeHttp::FSync() { - errno = ENOSYS; - return -1; -} - -int MountNodeHttp::GetDents(size_t offs, struct dirent* pdir, size_t count) { - errno = ENOSYS; - return -1; -} - -int MountNodeHttp::GetStat(struct stat* stat) { - AutoLock lock(&lock_); - - // Assume we need to 'HEAD' if we do not know the size, otherwise, assume - // that the information is constant. We can add a timeout if needed. - MountHttp* mount = static_cast<MountHttp*>(mount_); - if (stat_.st_size == 0 || !mount->cache_stat_) { - StringMap_t headers; - PP_Resource loader; - PP_Resource request; - PP_Resource response; - int32_t statuscode; - StringMap_t response_headers; - if (!OpenUrl("HEAD", &headers, &loader, &request, &response, &statuscode, - &response_headers)) { - // errno is already set by OpenUrl. - return -1; - } - - ScopedResource scoped_loader(mount_->ppapi(), loader); - ScopedResource scoped_request(mount_->ppapi(), request); - ScopedResource scoped_response(mount_->ppapi(), response); - - - size_t entity_length; - if (ParseContentLength(response_headers, &entity_length)) { - SetCachedSize(static_cast<off_t>(entity_length)); - } else if (cache_content_ && !has_cached_size_) { - DownloadToCache(); - } else { - // Don't use SetCachedSize here -- it is actually unknown. - stat_.st_size = 0; - } - - stat_.st_atime = 0; // TODO(binji): Use "Last-Modified". - stat_.st_mtime = 0; - stat_.st_ctime = 0; - } - - // Fill the stat structure if provided - if (stat) { - memcpy(stat, &stat_, sizeof(stat_)); - } - return 0; -} - -int MountNodeHttp::Read(size_t offs, void* buf, size_t count) { - AutoLock lock(&lock_); - if (cache_content_) { - if (cached_data_.empty()) { - if (DownloadToCache() < 0) - return -1; - } - - return ReadPartialFromCache(offs, buf, count); - } - - return DownloadPartial(offs, buf, count); -} - -int MountNodeHttp::FTruncate(off_t size) { - errno = ENOSYS; - return -1; -} - -int MountNodeHttp::Write(size_t offs, const void* buf, size_t count) { - // TODO(binji): support POST? - errno = ENOSYS; - return -1; -} - -size_t MountNodeHttp::GetSize() { - // TODO(binji): This value should be cached properly; i.e. obey the caching - // headers returned by the server. - AutoLock lock(&lock_); - if (!has_cached_size_) { - // Even if DownloadToCache fails, the best result we can return is what - // was written to stat_.st_size. - if (cache_content_) - DownloadToCache(); - } - - return stat_.st_size; -} - -MountNodeHttp::MountNodeHttp(Mount* mount, const std::string& url, - bool cache_content) - : MountNode(mount), - url_(url), - cache_content_(cache_content), - has_cached_size_(false) { -} - -bool MountNodeHttp::OpenUrl(const char* method, - StringMap_t* request_headers, - PP_Resource* out_loader, - PP_Resource* out_request, - PP_Resource* out_response, - int32_t* out_statuscode, - StringMap_t* out_response_headers) { - // Assume lock_ is already held. - - PepperInterface* ppapi = mount_->ppapi(); - - MountHttp* mount_http = static_cast<MountHttp*>(mount_); - ScopedResource request(ppapi, - mount_http->MakeUrlRequestInfo(url_, method, - request_headers)); - if (!request.pp_resource()) { - errno = EINVAL; - return false; - } - - URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); - URLResponseInfoInterface* response_interface = - ppapi->GetURLResponseInfoInterface(); - VarInterface* var_interface = ppapi->GetVarInterface(); - - ScopedResource loader(ppapi, loader_interface->Create(ppapi->GetInstance())); - if (!loader.pp_resource()) { - errno = EINVAL; - return false; - } - - int32_t result = loader_interface->Open( - loader.pp_resource(), request.pp_resource(), PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return false; - } - - ScopedResource response( - ppapi, - loader_interface->GetResponseInfo(loader.pp_resource())); - if (!response.pp_resource()) { - errno = EINVAL; - return false; - } - - // Get response statuscode. - PP_Var statuscode = response_interface->GetProperty( - response.pp_resource(), - PP_URLRESPONSEPROPERTY_STATUSCODE); - - if (statuscode.type != PP_VARTYPE_INT32) { - errno = EINVAL; - return false; - } - - *out_statuscode = statuscode.value.as_int; - - // Only accept OK or Partial Content. - if (*out_statuscode != STATUSCODE_OK && - *out_statuscode != STATUSCODE_PARTIAL_CONTENT) { - errno = EINVAL; - return false; - } - - // Get response headers. - PP_Var response_headers_var = response_interface->GetProperty( - response.pp_resource(), - PP_URLRESPONSEPROPERTY_HEADERS); - - uint32_t response_headers_length; - const char* response_headers_str = var_interface->VarToUtf8( - response_headers_var, - &response_headers_length); - - *out_loader = loader.Release(); - *out_request = request.Release(); - *out_response = response.Release(); - *out_response_headers = ParseHeaders(response_headers_str, - response_headers_length); - - return true; -} - -int MountNodeHttp::DownloadToCache() { - StringMap_t headers; - PP_Resource loader; - PP_Resource request; - PP_Resource response; - int32_t statuscode; - StringMap_t response_headers; - if (!OpenUrl("GET", &headers, &loader, &request, &response, &statuscode, - &response_headers)) { - // errno is already set by OpenUrl. - return -1; - } - - PepperInterface* ppapi = mount_->ppapi(); - ScopedResource scoped_loader(ppapi, loader); - ScopedResource scoped_request(ppapi, request); - ScopedResource scoped_response(ppapi, response); - - size_t content_length = 0; - if (ParseContentLength(response_headers, &content_length)) { - cached_data_.resize(content_length); - int real_size = DownloadToBuffer(loader, cached_data_.data(), - content_length); - if (real_size < 0) - return -1; - - SetCachedSize(real_size); - cached_data_.resize(real_size); - return real_size; - } - - // We don't know how big the file is. Read in chunks. - cached_data_.resize(MAX_READ_BUFFER_SIZE); - size_t total_bytes_read = 0; - size_t bytes_to_read = MAX_READ_BUFFER_SIZE; - while (true) { - char* buf = cached_data_.data() + total_bytes_read; - int bytes_read = DownloadToBuffer(loader, buf, bytes_to_read); - if (bytes_read < 0) - return -1; - - total_bytes_read += bytes_read; - - if (bytes_read < bytes_to_read) { - SetCachedSize(total_bytes_read); - cached_data_.resize(total_bytes_read); - return total_bytes_read; - } - - cached_data_.resize(total_bytes_read + bytes_to_read); - } -} - -int MountNodeHttp::ReadPartialFromCache(size_t offs, void* buf, size_t count) { - if (offs > cached_data_.size()) { - errno = EINVAL; - return -1; - } - - count = std::min(count, cached_data_.size() - offs); - memcpy(buf, &cached_data_.data()[offs], count); - return count; -} - -int MountNodeHttp::DownloadPartial(size_t offs, void* buf, size_t count) { - StringMap_t headers; - - char buffer[100]; - // Range request is inclusive: 0-99 returns 100 bytes. - snprintf(&buffer[0], sizeof(buffer), "bytes=%"PRIuS"-%"PRIuS, - offs, offs + count - 1); - headers["Range"] = buffer; - - PP_Resource loader; - PP_Resource request; - PP_Resource response; - int32_t statuscode; - StringMap_t response_headers; - if (!OpenUrl("GET", &headers, &loader, &request, &response, &statuscode, - &response_headers)) { - // errno is already set by OpenUrl. - return -1; - } - - PepperInterface* ppapi = mount_->ppapi(); - ScopedResource scoped_loader(ppapi, loader); - ScopedResource scoped_request(ppapi, request); - ScopedResource scoped_response(ppapi, response); - - size_t read_start = 0; - if (statuscode == STATUSCODE_OK) { - // No partial result, read everything starting from the part we care about. - size_t content_length; - if (ParseContentLength(response_headers, &content_length)) { - if (offs >= content_length) { - errno = EINVAL; - return 0; - } +Error MountHttp::Open(const Path& path, int mode, MountNode** out_node) { + *out_node = NULL; - // Clamp count, if trying to read past the end of the file. - if (offs + count > content_length) { - count = content_length - offs; - } - } - } else if (statuscode == STATUSCODE_PARTIAL_CONTENT) { - // Determine from the headers where we are reading. - size_t read_end; - size_t entity_length; - if (ParseContentRange(response_headers, &read_start, &read_end, - &entity_length)) { - if (read_start > offs || read_start > read_end) { - // If this error occurs, the server is returning bogus values. - errno = EINVAL; - return -1; - } + assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/'); - // Clamp count, if trying to read past the end of the file. - count = std::min(read_end - read_start, count); - } else { - // Partial Content without Content-Range. Assume that the server gave us - // exactly what we asked for. This can happen even when the server - // returns 200 -- the cache may return 206 in this case, but not modify - // the headers. - read_start = offs; - } + NodeMap_t::iterator iter = node_cache_.find(path.Join()); + if (iter != node_cache_.end()) { + *out_node = iter->second; + return 0; } - if (read_start < offs) { - // We aren't yet at the location where we want to start reading. Read into - // our dummy buffer until then. - size_t bytes_to_read = offs - read_start; - if (buffer_.size() < bytes_to_read) - buffer_.resize(std::min(bytes_to_read, MAX_READ_BUFFER_SIZE)); - - while (bytes_to_read > 0) { - int32_t bytes_read = DownloadToBuffer(loader, buffer_.data(), - buffer_.size()); - if (bytes_read < 0) - return -1; + // If we can't find the node in the cache, create it + std::string url = url_root_ + (path.IsAbsolute() ? path.Range(1, path.Size()) + : path.Join()); - bytes_to_read -= bytes_read; - } + MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); + Error error = node->Init(mode); + if (error) { + node->Release(); + return error; } - return DownloadToBuffer(loader, buf, count); -} - -int MountNodeHttp::DownloadToBuffer(PP_Resource loader, void* buf, - size_t count) { - PepperInterface* ppapi = mount_->ppapi(); - URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); - - char* out_buffer = static_cast<char*>(buf); - size_t bytes_to_read = count; - while (bytes_to_read > 0) { - int32_t bytes_read = loader_interface->ReadResponseBody( - loader, out_buffer, bytes_to_read, PP_BlockUntilComplete()); - - if (bytes_read == 0) { - // This is not an error -- it may just be that we were trying to read - // more data than exists. - return count - bytes_to_read; - } - - if (bytes_read < 0) { - errno = PPErrorToErrno(bytes_read); - return -1; - } - - assert(bytes_read <= bytes_to_read); - bytes_to_read -= bytes_read; - out_buffer += bytes_read; + error = node->GetStat(NULL); + if (error) { + node->Release(); + return error; } - return count; -} - -MountNode *MountHttp::Open(const Path& path, int mode) { - assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/'); - - NodeMap_t::iterator iter = node_cache_.find(path.Join()); - if (iter != node_cache_.end()) { - return iter->second; + MountNodeDir* parent; + error = FindOrCreateDir(path.Parent(), &parent); + if (error) { + node->Release(); + return error; } - // If we can't find the node in the cache, create it - std::string url = url_root_ + (path.IsAbsolute() ? - path.Range(1, path.Size()) : - path.Join()); - - MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); - if (!node->Init(mode) || (0 != node->GetStat(NULL))) { + error = parent->AddChild(path.Basename(), node); + if (error) { node->Release(); - return NULL; + return error; } - MountNodeDir* parent = FindOrCreateDir(path.Parent()); node_cache_[path.Join()] = node; - parent->AddChild(path.Basename(), node); - return node; -} -int MountHttp::Unlink(const Path& path) { - errno = ENOSYS; - return -1; + *out_node = node; + return 0; } -int MountHttp::Mkdir(const Path& path, int permissions) { - errno = ENOSYS; - return -1; -} +Error MountHttp::Unlink(const Path& path) { return ENOSYS; } -int MountHttp::Rmdir(const Path& path) { - errno = ENOSYS; - return -1; -} +Error MountHttp::Mkdir(const Path& path, int permissions) { return ENOSYS; } -int MountHttp::Remove(const Path& path) { - errno = ENOSYS; - return -1; -} +Error MountHttp::Rmdir(const Path& path) { return ENOSYS; } + +Error MountHttp::Remove(const Path& path) { return ENOSYS; } -PP_Resource MountHttp::MakeUrlRequestInfo( - const std::string& url, - const char* method, - StringMap_t* additional_headers) { +PP_Resource MountHttp::MakeUrlRequestInfo(const std::string& url, + const char* method, + StringMap_t* additional_headers) { URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface(); VarInterface* var_interface = ppapi_->GetVarInterface(); @@ -618,21 +119,23 @@ PP_Resource MountHttp::MakeUrlRequestInfo( if (!request_info) return 0; - interface->SetProperty( - request_info, PP_URLREQUESTPROPERTY_URL, - var_interface->VarFromUtf8(url.c_str(), url.length())); - interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_METHOD, + interface->SetProperty(request_info, + PP_URLREQUESTPROPERTY_URL, + var_interface->VarFromUtf8(url.c_str(), url.length())); + interface->SetProperty(request_info, + PP_URLREQUESTPROPERTY_METHOD, var_interface->VarFromUtf8(method, strlen(method))); interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE)); - interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS, + interface->SetProperty(request_info, + PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS, PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE)); // Merge the mount headers with the request headers. If the field is already // set it |additional_headers|, don't use the one from headers_. for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end(); - ++iter) { + ++iter) { const std::string& key = NormalizeHeaderKey(iter->first); if (additional_headers->find(key) == additional_headers->end()) { additional_headers->insert(std::make_pair(key, iter->second)); @@ -642,12 +145,14 @@ PP_Resource MountHttp::MakeUrlRequestInfo( // Join the headers into one string. std::string headers; for (StringMap_t::iterator iter = additional_headers->begin(); - iter != additional_headers->end(); ++iter) { + iter != additional_headers->end(); + ++iter) { headers += iter->first + ": " + iter->second + '\n'; } interface->SetProperty( - request_info, PP_URLREQUESTPROPERTY_HEADERS, + request_info, + PP_URLREQUESTPROPERTY_HEADERS, var_interface->VarFromUtf8(headers.c_str(), headers.length())); return request_info; @@ -657,12 +162,12 @@ MountHttp::MountHttp() : allow_cors_(false), allow_credentials_(false), cache_stat_(true), - cache_content_(true) { -} + cache_content_(true) {} -bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { - if (!Mount::Init(dev, args, ppapi)) - return false; +Error MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { + Error error = Mount::Init(dev, args, ppapi); + if (error) + return error; // Parse mount args. for (StringMap_t::iterator iter = args.begin(); iter != args.end(); ++iter) { @@ -674,11 +179,18 @@ bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { url_root_ += '/'; } } else if (iter->first == "manifest") { - char *text = LoadManifest(iter->second); - if (text != NULL) { - ParseManifest(text); + char* text; + error = LoadManifest(iter->second, &text); + if (error) + return error; + + error = ParseManifest(text); + if (error) { delete[] text; + return error; } + + delete[] text; } else if (iter->first == "allow_cross_origin_requests") { allow_cors_ = iter->second == "true"; } else if (iter->first == "allow_credentials") { @@ -693,34 +205,53 @@ bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { } } - return true; + return 0; } -void MountHttp::Destroy() { -} +void MountHttp::Destroy() {} + +Error MountHttp::FindOrCreateDir(const Path& path, MountNodeDir** out_node) { + *out_node = NULL; -MountNodeDir* MountHttp::FindOrCreateDir(const Path& path) { std::string strpath = path.Join(); NodeMap_t::iterator iter = node_cache_.find(strpath); if (iter != node_cache_.end()) { - return static_cast<MountNodeDir*>(iter->second); + *out_node = static_cast<MountNodeDir*>(iter->second); + return 0; } - // If the node does not exist, create it, and add it to the node cache + // If the node does not exist, create it. MountNodeDir* node = new MountNodeDir(this); - node->Init(S_IREAD); - node_cache_[strpath] = node; + Error error = node->Init(S_IREAD); + if (error) { + node->Release(); + return error; + } // If not the root node, find the parent node and add it to the parent if (!path.Top()) { - MountNodeDir* parent = FindOrCreateDir(path.Parent()); - parent->AddChild(path.Basename(), node); + MountNodeDir* parent; + error = FindOrCreateDir(path.Parent(), &parent); + if (error) { + node->Release(); + return error; + } + + error = parent->AddChild(path.Basename(), node); + if (error) { + node->Release(); + return error; + } } - return node; + // Add it to the node cache. + node_cache_[strpath] = node; + + *out_node = node; + return 0; } -bool MountHttp::ParseManifest(char *text) { +Error MountHttp::ParseManifest(char* text) { StringList_t lines; SplitString(text, "\n", &lines); @@ -741,62 +272,102 @@ bool MountHttp::ParseManifest(char *text) { // Ignore EXEC bit int mode = S_IFREG; switch (modestr[0]) { - case '-': mode = S_IFREG; break; - case 'c': mode = S_IFCHR; break; + case '-': + mode = S_IFREG; + break; + case 'c': + mode = S_IFCHR; + break; default: fprintf(stderr, "Unable to parse type %s for %s.\n", modestr, name); - return false; + return EINVAL; } switch (modestr[1]) { - case '-': break; - case 'r': mode |= S_IREAD; break; + case '-': + break; + case 'r': + mode |= S_IREAD; + break; default: fprintf(stderr, "Unable to parse read %s for %s.\n", modestr, name); - return false; + return EINVAL; } switch (modestr[2]) { - case '-': break; - case 'w': mode |= S_IWRITE; break; + case '-': + break; + case 'w': + mode |= S_IWRITE; + break; default: fprintf(stderr, "Unable to parse write %s for %s.\n", modestr, name); - return false; + return EINVAL; } Path path(name); - std::string url = url_root_ + (path.IsAbsolute() ? - path.Range(1, path.Size()) : - path.Join()); + std::string url = + url_root_ + + (path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join()); MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); - node->Init(mode); + Error error = node->Init(mode); + if (error) { + node->Release(); + return error; + } + node->SetCachedSize(atoi(lenstr)); - MountNodeDir* dir_node = FindOrCreateDir(path.Parent()); - dir_node->AddChild(path.Basename(), node); + MountNodeDir* dir_node; + error = FindOrCreateDir(path.Parent(), &dir_node); + if (error) { + node->Release(); + return error; + } + + error = dir_node->AddChild(path.Basename(), node); + if (error) { + node->Release(); + return error; + } std::string pname = path.Join(); node_cache_[pname] = node; } } - return true; + return 0; } -char *MountHttp::LoadManifest(const std::string& manifest_name) { +Error MountHttp::LoadManifest(const std::string& manifest_name, + char** out_manifest) { Path manifest_path(manifest_name); - MountNode* manifest_node = Open(manifest_path, O_RDONLY); + MountNode* manifest_node = NULL; + *out_manifest = NULL; - if (manifest_node) { - char *text = new char[manifest_node->GetSize() + 1]; - off_t len = manifest_node->Read(0, text, manifest_node->GetSize()); + int error = Open(manifest_path, O_RDONLY, &manifest_node); + if (error) + return error; + + size_t size; + error = manifest_node->GetSize(&size); + if (error) { manifest_node->Release(); + return error; + } - text[len] = 0; - return text; + char* text = new char[size + 1]; + int len; + error = manifest_node->Read(0, text, size, &len); + if (error) { + manifest_node->Release(); + return error; } - fprintf(stderr, "Could not open manifest: %s\n", manifest_name.c_str()); - return NULL; + manifest_node->Release(); + text[len] = 0; + + *out_manifest = text; + return 0; } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.h b/native_client_sdk/src/libraries/nacl_io/mount_http.h index 3a842a041e..e10f1afcfe 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_http.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_http.h @@ -15,15 +15,17 @@ class MountNodeDir; class MountNodeHttp; class MountHttpMock; +std::string NormalizeHeaderKey(const std::string& s); + class MountHttp : public Mount { public: typedef std::map<std::string, MountNode*> NodeMap_t; - virtual MountNode *Open(const Path& path, int mode); - virtual int Unlink(const Path& path); - virtual int Mkdir(const Path& path, int permissions); - virtual int Rmdir(const Path& path); - virtual int Remove(const Path& path); + virtual Error Open(const Path& path, int mode, MountNode** out_node); + virtual Error Unlink(const Path& path); + virtual Error Mkdir(const Path& path, int permissions); + virtual Error Rmdir(const Path& path); + virtual Error Remove(const Path& path); PP_Resource MakeUrlRequestInfo(const std::string& url, const char* method, @@ -32,11 +34,11 @@ class MountHttp : public Mount { protected: MountHttp(); - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); - MountNodeDir* FindOrCreateDir(const Path& path); - char *LoadManifest(const std::string& path); - bool ParseManifest(char *text); + Error FindOrCreateDir(const Path& path, MountNodeDir** out_node); + Error LoadManifest(const std::string& path, char** out_manifest); + Error ParseManifest(char *text); private: std::string url_root_; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc index fa3035cbcc..356600b4ec 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc @@ -14,22 +14,24 @@ #include "nacl_io/mount_node_mem.h" #include "nacl_io/osstat.h" #include "nacl_io/path.h" -#include "utils/auto_lock.h" -#include "utils/ref_object.h" +#include "sdk_util/auto_lock.h" +#include "sdk_util/ref_object.h" -// TODO(noelallen) : Grab/Redefine these in the kernel object once available. -#define USR_ID 1002 -#define GRP_ID 1003 +MountMem::MountMem() : root_(NULL), max_ino_(0) {} -MountMem::MountMem() - : root_(NULL), - max_ino_(0) { -} +Error MountMem::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { + Error error = Mount::Init(dev, args, ppapi); + if (error) + return error; + + root_ = new MountNodeDir(this); + error = root_->Init(S_IREAD | S_IWRITE); + if (error) { + root_->Release(); + return error; + } -bool MountMem::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { - Mount::Init(dev, args, ppapi); - root_ = AllocatePath(S_IREAD | S_IWRITE); - return (bool) (root_ != NULL); + return 0; } void MountMem::Destroy() { @@ -38,173 +40,156 @@ void MountMem::Destroy() { root_ = NULL; } -MountNode* MountMem::AllocatePath(int mode) { - MountNode *ptr = new MountNodeDir(this); - if (!ptr->Init(mode)) { - ptr->Release(); - return NULL; - } - return ptr; -} - -MountNode* MountMem::AllocateData(int mode) { - MountNode* ptr = new MountNodeMem(this); - if (!ptr->Init(mode)) { - ptr->Release(); - return NULL; - } - return ptr; -} - -MountNode* MountMem::FindNode(const Path& path, int type) { +Error MountMem::FindNode(const Path& path, int type, MountNode** out_node) { MountNode* node = root_; // If there is no root there, we have an error. - if (node == NULL) { - errno = ENOTDIR; - return NULL; - } + if (node == NULL) + return ENOTDIR; // We are expecting an "absolute" path from this mount point. - if (!path.IsAbsolute()) { - errno = EINVAL; - return NULL; - } + if (!path.IsAbsolute()) + return EINVAL; // Starting at the root, traverse the path parts. for (size_t index = 1; node && index < path.Size(); index++) { // If not a directory, then we have an error so return. - if (!node->IsaDir()) { - errno = ENOTDIR; - return NULL; - } + if (!node->IsaDir()) + return ENOTDIR; // Find the child node - node = node->FindChild(path.Part(index)); + Error error = node->FindChild(path.Part(index), &node); + if (error) + return error; } - // node should be root, a found child, or a failed 'FindChild' - // which already has the correct errno set. - if (NULL == node) return NULL; - // If a directory is expected, but it's not a directory, then fail. - if ((type & S_IFDIR) && !node->IsaDir()) { - errno = ENOTDIR; - return NULL; - } + if ((type & S_IFDIR) && !node->IsaDir()) + return ENOTDIR; // If a file is expected, but it's not a file, then fail. - if ((type & S_IFREG) && node->IsaDir()) { - errno = EISDIR; - return NULL; - } + if ((type & S_IFREG) && node->IsaDir()) + return EISDIR; // We now have a valid object of the expected type, so return it. - return node; + *out_node = node; + return 0; } -MountNode* MountMem::Open(const Path& path, int mode) { +Error MountMem::Open(const Path& path, int mode, MountNode** out_node) { AutoLock lock(&lock_); - MountNode* node = FindNode(path); + MountNode* node = NULL; + *out_node = NULL; - if (NULL == node) { - // Now first find the parent directory to see if we can add it - MountNode* parent = FindNode(path.Parent(), S_IFDIR); - if (NULL == parent) return NULL; + Error error = FindNode(path, 0, &node); + if (error) { // If the node does not exist and we can't create it, fail - if ((mode & O_CREAT) == 0) return NULL; + if ((mode & O_CREAT) == 0) + return ENOENT; - // Otherwise, create it with a single reference - mode = OpenModeToPermission(mode); - node = AllocateData(mode); - if (NULL == node) return NULL; + // Now first find the parent directory to see if we can add it + MountNode* parent = NULL; + error = FindNode(path.Parent(), S_IFDIR, &parent); + if (error) + return error; + + // Create it with a single reference + node = new MountNodeMem(this); + error = node->Init(OpenModeToPermission(mode)); + if (error) { + node->Release(); + return error; + } - if (parent->AddChild(path.Basename(), node) == -1) { + error = parent->AddChild(path.Basename(), node); + if (error) { // Or if it fails, release it node->Release(); - return NULL; + return error; } - return node; + + *out_node = node; + return 0; } + // Directories can only be opened read-only. + if (node->IsaDir() && (mode & 3) != O_RDONLY) + return EISDIR; + // If we were expected to create it exclusively, fail - if (mode & O_EXCL) { - errno = EEXIST; - return NULL; - } + if (mode & O_EXCL) + return EEXIST; // Verify we got the requested permissions. int req_mode = OpenModeToPermission(mode); int obj_mode = node->GetMode() & OpenModeToPermission(O_RDWR); - if ((obj_mode & req_mode) != req_mode) { - errno = EACCES; - return NULL; - } + if ((obj_mode & req_mode) != req_mode) + return EACCES; // We opened it, so ref count it before passing it back. node->Acquire(); - return node; + *out_node = node; + return 0; } -int MountMem::Mkdir(const Path& path, int mode) { +Error MountMem::Mkdir(const Path& path, int mode) { AutoLock lock(&lock_); // We expect a Mount "absolute" path - if (!path.IsAbsolute()) { - errno = ENOENT; - return -1; - } + if (!path.IsAbsolute()) + return ENOENT; // The root of the mount is already created by the mount - if (path.Size() == 1) { - errno = EEXIST; - return -1; - } - - MountNode* parent = FindNode(path.Parent(), S_IFDIR); - MountNode* node; + if (path.Size() == 1) + return EEXIST; - // If we failed to find the parent, the error code is already set. - if (NULL == parent) return -1; + MountNode* parent = NULL; + int error = FindNode(path.Parent(), S_IFDIR, &parent); + if (error) + return error; - node = parent->FindChild(path.Basename()); - if (NULL != node) { - errno = EEXIST; - return -1; - } + MountNode* node = NULL; + error = parent->FindChild(path.Basename(), &node); + if (!error) + return EEXIST; - // Otherwise, create a new node and attempt to add it - mode = OpenModeToPermission(mode); + if (error != ENOENT) + return error; // Allocate a node, with a RefCount of 1. If added to the parent // it will get ref counted again. In either case, release the // recount we have on exit. - node = AllocatePath(S_IREAD | S_IWRITE); - if (NULL == node) return -1; + node = new MountNodeDir(this); + error = node->Init(S_IREAD | S_IWRITE); + if (error) { + node->Release(); + return error; + } - if (parent->AddChild(path.Basename(), node) == -1) { + error = parent->AddChild(path.Basename(), node); + if (error) { node->Release(); - return -1; + return error; } node->Release(); return 0; } -int MountMem::Unlink(const Path& path) { +Error MountMem::Unlink(const Path& path) { return RemoveInternal(path, REMOVE_FILE); } -int MountMem::Rmdir(const Path& path) { +Error MountMem::Rmdir(const Path& path) { return RemoveInternal(path, REMOVE_DIR); } -int MountMem::Remove(const Path& path) { +Error MountMem::Remove(const Path& path) { return RemoveInternal(path, REMOVE_ALL); } -int MountMem::RemoveInternal(const Path& path, int remove_type) { +Error MountMem::RemoveInternal(const Path& path, int remove_type) { AutoLock lock(&lock_); bool dir_only = remove_type == REMOVE_DIR; bool file_only = remove_type == REMOVE_FILE; @@ -212,40 +197,33 @@ int MountMem::RemoveInternal(const Path& path, int remove_type) { if (dir_only) { // We expect a Mount "absolute" path - if (!path.IsAbsolute()) { - errno = ENOENT; - return -1; - } + if (!path.IsAbsolute()) + return ENOENT; // The root of the mount is already created by the mount - if (path.Size() == 1) { - errno = EEXIST; - return -1; - } + if (path.Size() == 1) + return EEXIST; } - MountNode* parent = FindNode(path.Parent(), S_IFDIR); - - // If we failed to find the parent, the error code is already set. - if (NULL == parent) return -1; + MountNode* parent = NULL; + int error = FindNode(path.Parent(), S_IFDIR, &parent); + if (error) + return error; // Verify we find a child which is a directory. - MountNode* child = parent->FindChild(path.Basename()); - if (NULL == child) { - errno = ENOENT; - return -1; - } - if (dir_only && !child->IsaDir()) { - errno = ENOTDIR; - return -1; - } - if (file_only && child->IsaDir()) { - errno = EISDIR; - return -1; - } - if (remove_dir && child->ChildCount() > 0) { - errno = ENOTEMPTY; - return -1; - } + MountNode* child = NULL; + error = parent->FindChild(path.Basename(), &child); + if (error) + return error; + + if (dir_only && !child->IsaDir()) + return ENOTDIR; + + if (file_only && child->IsaDir()) + return EISDIR; + + if (remove_dir && child->ChildCount() > 0) + return ENOTEMPTY; + return parent->RemoveChild(path.Basename()); } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_mem.h b/native_client_sdk/src/libraries/nacl_io/mount_mem.h index dc75e81769..930e9c734a 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_mem.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.h @@ -14,35 +14,33 @@ class MountMem : public Mount { protected: MountMem(); - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); // The protected functions are only used internally and will not // acquire or release the mount's lock themselves. The caller is // required to use correct locking as needed. - MountNode *AllocateData(int mode); - MountNode *AllocatePath(int mode); // Allocate or free an INODE number. int AllocateINO(); void FreeINO(int ino); // Find a Node specified node optionally failing if type does not match. - virtual MountNode* FindNode(const Path& path, int type = 0); + virtual Error FindNode(const Path& path, int type, MountNode** out_node); public: - virtual MountNode *Open(const Path& path, int mode); - virtual int Unlink(const Path& path); - virtual int Mkdir(const Path& path, int perm); - virtual int Rmdir(const Path& path); - virtual int Remove(const Path& path); + virtual Error Open(const Path& path, int mode, MountNode** out_node); + virtual Error Unlink(const Path& path); + virtual Error Mkdir(const Path& path, int perm); + virtual Error Rmdir(const Path& path); + virtual Error Remove(const Path& path); private: static const int REMOVE_DIR = 1; static const int REMOVE_FILE = 2; static const int REMOVE_ALL = REMOVE_DIR | REMOVE_FILE; - int RemoveInternal(const Path& path, int remove_type); + Error RemoveInternal(const Path& path, int remove_type); MountNode* root_; size_t max_ino_; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.cc b/native_client_sdk/src/libraries/nacl_io/mount_node.cc index ff722b44eb..34de7a8164 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.cc @@ -13,13 +13,12 @@ #include "nacl_io/kernel_wrap_real.h" #include "nacl_io/mount.h" #include "nacl_io/osmman.h" -#include "utils/auto_lock.h" +#include "sdk_util/auto_lock.h" static const int USR_ID = 1001; static const int GRP_ID = 1002; -MountNode::MountNode(Mount* mount) - : mount_(mount) { +MountNode::MountNode(Mount* mount) : mount_(mount) { memset(&stat_, 0, sizeof(stat_)); stat_.st_gid = GRP_ID; stat_.st_uid = USR_ID; @@ -32,12 +31,11 @@ MountNode::MountNode(Mount* mount) stat_.st_ino = 1; } -MountNode::~MountNode() { -} +MountNode::~MountNode() {} -bool MountNode::Init(int perm) { +Error MountNode::Init(int perm) { stat_.st_mode |= perm; - return true; + return 0; } void MountNode::Destroy() { @@ -46,119 +44,110 @@ void MountNode::Destroy() { } } -int MountNode::FSync() { - return 0; -} +Error MountNode::FSync() { return 0; } -int MountNode::FTruncate(off_t length) { - errno = EINVAL; - return -1; +Error MountNode::FTruncate(off_t length) { + return EINVAL; } -int MountNode::GetDents(size_t offs, struct dirent* pdir, size_t count) { - errno = ENOTDIR; - return -1; +Error MountNode::GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes) { + *out_bytes = 0; + return ENOTDIR; } -int MountNode::GetStat(struct stat* pstat) { +Error MountNode::GetStat(struct stat* pstat) { AutoLock lock(&lock_); memcpy(pstat, &stat_, sizeof(stat_)); return 0; } -int MountNode::Ioctl(int request, char* arg) { - errno = EINVAL; - return -1; +Error MountNode::Ioctl(int request, char* arg) { + return EINVAL; } -int MountNode::Read(size_t offs, void* buf, size_t count) { - errno = EINVAL; - return -1; +Error MountNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; + return EINVAL; } -int MountNode::Write(size_t offs, const void* buf, size_t count) { - errno = EINVAL; - return -1; +Error MountNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + return EINVAL; } -void* MountNode::MMap(void* addr, size_t length, int prot, int flags, - size_t offset) { +Error MountNode::MMap(void* addr, + size_t length, + int prot, + int flags, + size_t offset, + void** out_addr) { + *out_addr = NULL; + // Never allow mmap'ing PROT_EXEC. The passthrough node supports this, but we // don't. Fortunately, glibc will fallback if this fails, so dlopen will // continue to work. - if (prot & PROT_EXEC) { - errno = EPERM; - return MAP_FAILED; - } + if (prot & PROT_EXEC) + return EPERM; // This default mmap support is just enough to make dlopen work. // This implementation just reads from the mount into the mmap'd memory area. void* new_addr = addr; - int err = _real_mmap(&new_addr, length, prot | PROT_WRITE, flags | - MAP_ANONYMOUS, -1, 0); + int mmap_error = _real_mmap( + &new_addr, length, prot | PROT_WRITE, flags | MAP_ANONYMOUS, -1, 0); if (new_addr == MAP_FAILED) { _real_munmap(new_addr, length); - errno = err; - return MAP_FAILED; + return mmap_error; } - ssize_t cnt = Read(offset, new_addr, length); - if (cnt == -1) { + int bytes_read; + Error read_error = Read(offset, new_addr, length, &bytes_read); + if (read_error) { _real_munmap(new_addr, length); - errno = ENOSYS; - return MAP_FAILED; + return read_error; } - return new_addr; -} - -int MountNode::GetLinks() { - return stat_.st_nlink; + *out_addr = new_addr; + return 0; } -int MountNode::GetMode() { - return stat_.st_mode & ~S_IFMT; -} +int MountNode::GetLinks() { return stat_.st_nlink; } -size_t MountNode::GetSize() { - return stat_.st_size; -} +int MountNode::GetMode() { return stat_.st_mode & ~S_IFMT; } -int MountNode::GetType() { - return stat_.st_mode & S_IFMT; +Error MountNode::GetSize(size_t* out_size) { + *out_size = stat_.st_size; + return 0; } -bool MountNode::IsaDir() { - return (stat_.st_mode & S_IFDIR) != 0; -} +int MountNode::GetType() { return stat_.st_mode & S_IFMT; } -bool MountNode::IsaFile() { - return (stat_.st_mode & S_IFREG) != 0; -} +bool MountNode::IsaDir() { return (stat_.st_mode & S_IFDIR) != 0; } -bool MountNode::IsaTTY() { - return (stat_.st_mode & S_IFCHR) != 0; -} +bool MountNode::IsaFile() { return (stat_.st_mode & S_IFREG) != 0; } +bool MountNode::IsaTTY() { return (stat_.st_mode & S_IFCHR) != 0; } -int MountNode:: AddChild(const std::string& name, MountNode* node) { - errno = ENOTDIR; - return -1; +Error MountNode::AddChild(const std::string& name, MountNode* node) { + return ENOTDIR; } -int MountNode::RemoveChild(const std::string& name) { - errno = ENOTDIR; - return -1; +Error MountNode::RemoveChild(const std::string& name) { + return ENOTDIR; } -MountNode* MountNode::FindChild(const std::string& name) { - errno = ENOTDIR; - return NULL; +Error MountNode::FindChild(const std::string& name, MountNode** out_node) { + *out_node = NULL; + return ENOTDIR; } int MountNode::ChildCount() { - errno = ENOTDIR; - return -1; + return 0; } void MountNode::Link() { diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.h b/native_client_sdk/src/libraries/nacl_io/mount_node.h index 36a937e5fb..0acce66f80 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.h @@ -7,13 +7,16 @@ #include <string> +#include "nacl_io/error.h" #include "nacl_io/osstat.h" -#include "utils/ref_object.h" +#include "sdk_util/ref_object.h" struct dirent; struct stat; class Mount; +// NOTE: The KernelProxy is the only class that should be setting errno. All +// other classes should return Error (as defined by nacl_io/error.h). class MountNode : public RefObject { protected: explicit MountNode(Mount* mount); @@ -21,27 +24,46 @@ class MountNode : public RefObject { protected: // Initialize with node specific flags, in this case stat permissions. - virtual bool Init(int flags); + virtual Error Init(int flags); virtual void Destroy(); public: // Normal OS operations on a node (file), can be called by the kernel // directly so it must lock and unlock appropriately. These functions // must not be called by the mount. - virtual int FSync(); - virtual int FTruncate(off_t length); - virtual int GetDents(size_t offs, struct dirent* pdir, size_t count); - virtual int GetStat(struct stat* stat); - virtual int Ioctl(int request, char* arg); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); - virtual void* MMap(void* addr, size_t length, int prot, int flags, - size_t offset); + virtual Error FSync(); + // It is expected that the derived MountNode will fill with 0 when growing + // the file. + virtual Error FTruncate(off_t length); + // Assume that |out_bytes| is non-NULL. + virtual Error GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes); + // Assume that |stat| is non-NULL. + virtual Error GetStat(struct stat* stat); + // Assume that |arg| is non-NULL. + virtual Error Ioctl(int request, char* arg); + // Assume that |buf| and |out_bytes| are non-NULL. + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + // Assume that |buf| and |out_bytes| are non-NULL. + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + // Assume that |addr| and |out_addr| are non-NULL. + virtual Error MMap(void* addr, + size_t length, + int prot, + int flags, + size_t offset, + void** out_addr); virtual int GetLinks(); virtual int GetMode(); virtual int GetType(); - virtual size_t GetSize(); + // Assume that |out_size| is non-NULL. + virtual Error GetSize(size_t *out_size); virtual bool IsaDir(); virtual bool IsaFile(); virtual bool IsaTTY(); @@ -51,18 +73,20 @@ class MountNode : public RefObject { // must be held while these calls are made. // Adds or removes a directory entry updating the link numbers and refcount - virtual int AddChild(const std::string& name, MountNode *node); - virtual int RemoveChild(const std::string& name); + // Assumes that |node| is non-NULL. + virtual Error AddChild(const std::string& name, MountNode* node); + virtual Error RemoveChild(const std::string& name); // Find a child and return it without updating the refcount - virtual MountNode* FindChild(const std::string& name); + // Assumes that |out_node| is non-NULL. + virtual Error FindChild(const std::string& name, MountNode** out_node); virtual int ChildCount(); // Update the link count virtual void Link(); virtual void Unlink(); -protected: + protected: struct stat stat_; Mount* mount_; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc index 7a33bfd684..6f7feab44a 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc @@ -9,78 +9,82 @@ #include "nacl_io/osdirent.h" #include "nacl_io/osstat.h" -#include "utils/macros.h" -#include "utils/auto_lock.h" +#include "sdk_util/auto_lock.h" +#include "sdk_util/macros.h" -MountNodeDir::MountNodeDir(Mount* mount) - : MountNode(mount), - cache_(NULL) { +MountNodeDir::MountNodeDir(Mount* mount) : MountNode(mount), cache_(NULL) { stat_.st_mode |= S_IFDIR; } MountNodeDir::~MountNodeDir() { + for (MountNodeMap_t::iterator it = map_.begin(); it != map_.end(); ++it) { + it->second->Unlink(); + } free(cache_); } -int MountNodeDir::Read(size_t offs, void *buf, size_t count) { - errno = EISDIR; - return -1; +Error MountNodeDir::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; + return EISDIR; } -int MountNodeDir::FTruncate(off_t size) { - errno = EISDIR; - return -1; -} +Error MountNodeDir::FTruncate(off_t size) { return EISDIR; } -int MountNodeDir::Write(size_t offs, void *buf, size_t count) { - errno = EISDIR; - return -1; +Error MountNodeDir::Write(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + return EISDIR; } -int MountNodeDir::GetDents(size_t offs, struct dirent* pdir, size_t size) { +Error MountNodeDir::GetDents(size_t offs, + struct dirent* pdir, + size_t size, + int* out_bytes) { + *out_bytes = 0; + AutoLock lock(&lock_); // If the buffer pointer is invalid, fail - if (NULL == pdir) { - errno = EINVAL; - return -1; - } + if (NULL == pdir) + return EINVAL; // If the buffer is too small, fail - if (size < sizeof(struct dirent)) { - errno = EINVAL; - return -1; - } + if (size < sizeof(struct dirent)) + return EINVAL; // Force size to a multiple of dirent size -= size % sizeof(struct dirent); size_t max = map_.size() * sizeof(struct dirent); - if (cache_ == NULL) BuildCache(); + if (cache_ == NULL) + BuildCache(); - if (offs >= max) return 0; - if (offs + size >= max) size = max - offs; + if (offs >= max) { + // OK, trying to read past the end. + return 0; + } - memcpy(pdir, ((char *) cache_) + offs, size); - return size; + if (offs + size >= max) + size = max - offs; + + memcpy(pdir, ((char*)cache_) + offs, size); + *out_bytes = size; + return 0; } -int MountNodeDir::AddChild(const std::string& name, MountNode* node) { +Error MountNodeDir::AddChild(const std::string& name, MountNode* node) { AutoLock lock(&lock_); - if (name.empty()) { - errno = ENOENT; - return -1; - } - if (name.length() >= MEMBER_SIZE(struct dirent, d_name)) { - errno = ENAMETOOLONG; - return -1; - } + if (name.empty()) + return ENOENT; + + if (name.length() >= MEMBER_SIZE(struct dirent, d_name)) + return ENAMETOOLONG; MountNodeMap_t::iterator it = map_.find(name); - if (it != map_.end()) { - errno = EEXIST; - return -1; - } + if (it != map_.end()) + return EEXIST; node->Link(); map_[name] = node; @@ -88,7 +92,7 @@ int MountNodeDir::AddChild(const std::string& name, MountNode* node) { return 0; } -int MountNodeDir::RemoveChild(const std::string& name) { +Error MountNodeDir::RemoveChild(const std::string& name) { AutoLock lock(&lock_); MountNodeMap_t::iterator it = map_.find(name); if (it != map_.end()) { @@ -97,18 +101,19 @@ int MountNodeDir::RemoveChild(const std::string& name) { ClearCache(); return 0; } - errno = ENOENT; - return -1; + return ENOENT; } -MountNode* MountNodeDir::FindChild(const std::string& name) { +Error MountNodeDir::FindChild(const std::string& name, MountNode** out_node) { + *out_node = NULL; + AutoLock lock(&lock_); MountNodeMap_t::iterator it = map_.find(name); - if (it != map_.end()) { - return it->second; - } - errno = ENOENT; - return NULL; + if (it == map_.end()) + return ENOENT; + + *out_node = it->second; + return 0; } int MountNodeDir::ChildCount() { @@ -123,7 +128,7 @@ void MountNodeDir::ClearCache() { void MountNodeDir::BuildCache() { if (map_.size()) { - cache_ = (struct dirent *) malloc(sizeof(struct dirent) * map_.size()); + cache_ = (struct dirent*)malloc(sizeof(struct dirent) * map_.size()); MountNodeMap_t::iterator it = map_.begin(); for (size_t index = 0; it != map_.end(); it++, index++) { MountNode* node = it->second; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h index b6af58dda2..a2ab67ecfa 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h @@ -25,15 +25,18 @@ class MountNodeDir : public MountNode { public: typedef std::map<std::string, MountNode*> MountNodeMap_t; - virtual int FTruncate(off_t size); - virtual int GetDents(size_t offs, struct dirent* pdir, size_t count); - virtual int Read(size_t offs, void *buf, size_t count); - virtual int Write(size_t offs, void *buf, size_t count); + virtual Error FTruncate(off_t size); + virtual Error GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes); + virtual Error Read(size_t offs, void *buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, void *buf, size_t count, int* out_bytes); // Adds a finds or adds a directory entry as an INO, updating the refcount - virtual int AddChild(const std::string& name, MountNode *node); - virtual int RemoveChild(const std::string& name); - virtual MountNode* FindChild(const std::string& name); + virtual Error AddChild(const std::string& name, MountNode *node); + virtual Error RemoveChild(const std::string& name); + virtual Error FindChild(const std::string& name, MountNode** out_node); virtual int ChildCount(); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc index fb0d5ed2fb..8be2a4f74b 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc @@ -17,7 +17,7 @@ #include "nacl_io/mount.h" #include "nacl_io/osdirent.h" #include "nacl_io/pepper_interface.h" -#include "utils/auto_lock.h" +#include "sdk_util/auto_lock.h" namespace { @@ -55,56 +55,61 @@ int32_t ModeToOpenFlags(int mode) { break; } - if (mode & O_CREAT) open_flags |= PP_FILEOPENFLAG_CREATE; - if (mode & O_TRUNC) open_flags |= PP_FILEOPENFLAG_TRUNCATE; - if (mode & O_EXCL) open_flags |= PP_FILEOPENFLAG_EXCLUSIVE; + if (mode & O_CREAT) + open_flags |= PP_FILEOPENFLAG_CREATE; + if (mode & O_TRUNC) + open_flags |= PP_FILEOPENFLAG_TRUNCATE; + if (mode & O_EXCL) + open_flags |= PP_FILEOPENFLAG_EXCLUSIVE; return open_flags; } } // namespace -int MountNodeHtml5Fs::FSync() { - int32_t result = mount_->ppapi()->GetFileIoInterface()->Flush( - fileio_resource_, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } +Error MountNodeHtml5Fs::FSync() { + // Cannot call Flush on a directory; simply do nothing. + if (IsDirectory()) + return 0; + int32_t result = mount_->ppapi()->GetFileIoInterface() + ->Flush(fileio_resource_, PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); return 0; } -int MountNodeHtml5Fs::GetDents(size_t offs, struct dirent* pdir, size_t size) { +Error MountNodeHtml5Fs::GetDents(size_t offs, + struct dirent* pdir, + size_t size, + int* out_bytes) { + *out_bytes = 0; + // If the buffer pointer is invalid, fail - if (NULL == pdir) { - errno = EINVAL; - return -1; - } + if (NULL == pdir) + return EINVAL; // If the buffer is too small, fail - if (size < sizeof(struct dirent)) { - errno = EINVAL; - return -1; - } + if (size < sizeof(struct dirent)) + return EINVAL; - OutputBuffer output_buf = { NULL, 0 }; - PP_ArrayOutput output = { &GetOutputBuffer, &output_buf }; - int32_t result = - mount_->ppapi()->GetFileRefInterface()->ReadDirectoryEntries( - fileref_resource_, output, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } + // If this is not a directory, fail + if (!IsDirectory()) + return ENOTDIR; + + OutputBuffer output_buf = {NULL, 0}; + PP_ArrayOutput output = {&GetOutputBuffer, &output_buf}; + int32_t result = mount_->ppapi()->GetFileRefInterface()->ReadDirectoryEntries( + fileref_resource_, output, PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); std::vector<struct dirent> dirents; - PP_DirectoryEntry* entries = - static_cast<PP_DirectoryEntry*>(output_buf.data); + PP_DirectoryEntry* entries = static_cast<PP_DirectoryEntry*>(output_buf.data); for (int i = 0; i < output_buf.element_count; ++i) { - PP_Var file_name_var = mount_->ppapi()->GetFileRefInterface()->GetName( - entries[i].file_ref); + PP_Var file_name_var = + mount_->ppapi()->GetFileRefInterface()->GetName(entries[i].file_ref); // Release the file reference. mount_->ppapi()->ReleaseResource(entries[i].file_ref); @@ -113,18 +118,18 @@ int MountNodeHtml5Fs::GetDents(size_t offs, struct dirent* pdir, size_t size) { continue; uint32_t file_name_length; - const char* file_name = mount_->ppapi()->GetVarInterface()->VarToUtf8( - file_name_var, &file_name_length); + const char* file_name = mount_->ppapi()->GetVarInterface() + ->VarToUtf8(file_name_var, &file_name_length); if (!file_name) continue; file_name_length = std::min( static_cast<size_t>(file_name_length), - sizeof(static_cast<struct dirent*>(0)->d_name) - 1); // -1 for NULL. + sizeof(static_cast<struct dirent*>(0)->d_name) - 1); // -1 for NULL. dirents.push_back(dirent()); struct dirent& direntry = dirents.back(); - direntry.d_ino = 0; // TODO(binji): Is this needed? + direntry.d_ino = 1; // Must be > 0. direntry.d_off = sizeof(struct dirent); direntry.d_reclen = sizeof(struct dirent); strncpy(direntry.d_name, file_name, file_name_length); @@ -138,33 +143,40 @@ int MountNodeHtml5Fs::GetDents(size_t offs, struct dirent* pdir, size_t size) { size -= size % sizeof(struct dirent); size_t max = dirents.size() * sizeof(struct dirent); - if (offs >= max) return 0; - if (offs + size >= max) size = max - offs; + if (offs >= max) + return 0; + + if (offs + size >= max) + size = max - offs; memcpy(pdir, reinterpret_cast<char*>(dirents.data()) + offs, size); + *out_bytes = size; return 0; } -int MountNodeHtml5Fs::GetStat(struct stat* stat) { +Error MountNodeHtml5Fs::GetStat(struct stat* stat) { AutoLock lock(&lock_); PP_FileInfo info; - int32_t result = mount_->ppapi()->GetFileIoInterface()->Query( - fileio_resource_, &info, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } + int32_t result = mount_->ppapi()->GetFileRefInterface()->Query( + fileref_resource_, &info, PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); // Fill in known info here. memcpy(stat, &stat_, sizeof(stat_)); // Fill in the additional info from ppapi. switch (info.type) { - case PP_FILETYPE_REGULAR: stat->st_mode |= S_IFREG; break; - case PP_FILETYPE_DIRECTORY: stat->st_mode |= S_IFDIR; break; + case PP_FILETYPE_REGULAR: + stat->st_mode |= S_IFREG; + break; + case PP_FILETYPE_DIRECTORY: + stat->st_mode |= S_IFDIR; + break; case PP_FILETYPE_OTHER: - default: break; + default: + break; } stat->st_size = static_cast<off_t>(info.size); stat->st_atime = info.last_access_time; @@ -174,78 +186,107 @@ int MountNodeHtml5Fs::GetStat(struct stat* stat) { return 0; } -int MountNodeHtml5Fs::Read(size_t offs, void* buf, size_t count) { - int32_t result = mount_->ppapi()->GetFileIoInterface()->Read( - fileio_resource_, offs, static_cast<char*>(buf), - static_cast<int32_t>(count), - PP_BlockUntilComplete()); - if (result < 0) { - errno = PPErrorToErrno(result); - return -1; - } +Error MountNodeHtml5Fs::Read(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + + if (IsDirectory()) + return EISDIR; - return result; + int32_t result = + mount_->ppapi()->GetFileIoInterface()->Read(fileio_resource_, + offs, + static_cast<char*>(buf), + static_cast<int32_t>(count), + PP_BlockUntilComplete()); + if (result < 0) + return PPErrorToErrno(result); + + *out_bytes = result; + return 0; } -int MountNodeHtml5Fs::FTruncate(off_t size) { - int32_t result = mount_->ppapi()->GetFileIoInterface()->SetLength( - fileio_resource_, size, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } +Error MountNodeHtml5Fs::FTruncate(off_t size) { + if (IsDirectory()) + return EISDIR; + int32_t result = mount_->ppapi()->GetFileIoInterface() + ->SetLength(fileio_resource_, size, PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); return 0; } -int MountNodeHtml5Fs::Write(size_t offs, const void* buf, size_t count) { - int32_t result = mount_->ppapi()->GetFileIoInterface()->Write( - fileio_resource_, offs, static_cast<const char*>(buf), - static_cast<int32_t>(count), PP_BlockUntilComplete()); - if (result < 0) { - errno = PPErrorToErrno(result); - return -1; - } - - return result; +Error MountNodeHtml5Fs::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + + if (IsDirectory()) + return EISDIR; + + int32_t result = mount_->ppapi()->GetFileIoInterface() + ->Write(fileio_resource_, + offs, + static_cast<const char*>(buf), + static_cast<int32_t>(count), + PP_BlockUntilComplete()); + if (result < 0) + return PPErrorToErrno(result); + + *out_bytes = result; + return 0; } -size_t MountNodeHtml5Fs::GetSize() { +Error MountNodeHtml5Fs::GetSize(size_t* out_size) { + *out_size = 0; + AutoLock lock(&lock_); PP_FileInfo info; - int32_t result = mount_->ppapi()->GetFileIoInterface()->Query( - fileio_resource_, &info, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } + int32_t result = mount_->ppapi()->GetFileIoInterface() + ->Query(fileio_resource_, &info, PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); - return static_cast<size_t>(info.size); + *out_size = static_cast<size_t>(info.size); + return 0; } MountNodeHtml5Fs::MountNodeHtml5Fs(Mount* mount, PP_Resource fileref_resource) : MountNode(mount), fileref_resource_(fileref_resource), - fileio_resource_(0) { -} - -bool MountNodeHtml5Fs::Init(int perm) { - if (!MountNode::Init(Mount::OpenModeToPermission(perm))) - return false; - - fileio_resource_= mount_->ppapi()->GetFileIoInterface()->Create( - mount_->ppapi()->GetInstance()); + fileio_resource_(0) {} + +Error MountNodeHtml5Fs::Init(int perm) { + Error error = MountNode::Init(Mount::OpenModeToPermission(perm)); + if (error) + return error; + + // First query the FileRef to see if it is a file or directory. + PP_FileInfo file_info; + mount_->ppapi()->GetFileRefInterface()->Query(fileref_resource_, &file_info, + PP_BlockUntilComplete()); + // If this is a directory, do not get a FileIO. + if (file_info.type == PP_FILETYPE_DIRECTORY) + return 0; + + fileio_resource_ = mount_->ppapi()->GetFileIoInterface() + ->Create(mount_->ppapi()->GetInstance()); if (!fileio_resource_) - return false; + return ENOSYS; - int32_t open_result = mount_->ppapi()->GetFileIoInterface()->Open( - fileio_resource_, fileref_resource_, ModeToOpenFlags(perm), - PP_BlockUntilComplete()); + int32_t open_result = + mount_->ppapi()->GetFileIoInterface()->Open(fileio_resource_, + fileref_resource_, + ModeToOpenFlags(perm), + PP_BlockUntilComplete()); if (open_result != PP_OK) - return false; - - return true; + return PPErrorToErrno(open_result); + return 0; } void MountNodeHtml5Fs::Destroy() { diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h index 58b4d13d7e..832d3d40ce 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h @@ -15,25 +15,36 @@ class MountNodeHtml5Fs : public MountNode { // Normal OS operations on a node (file), can be called by the kernel // directly so it must lock and unlock appropriately. These functions // must not be called by the mount. - virtual int FSync(); - virtual int GetDents(size_t offs, struct dirent* pdir, size_t count); - virtual int GetStat(struct stat* stat); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int FTruncate(off_t size); - virtual int Write(size_t offs, const void* buf, size_t count); - - virtual size_t GetSize(); + virtual Error FSync(); + virtual Error GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes); + virtual Error GetStat(struct stat* stat); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error FTruncate(off_t size); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + + virtual Error GetSize(size_t *out_size); protected: MountNodeHtml5Fs(Mount* mount, PP_Resource fileref); // Init with standard open flags - virtual bool Init(int o_mode); + virtual Error Init(int o_mode); virtual void Destroy(); private: PP_Resource fileref_resource_; - PP_Resource fileio_resource_; + PP_Resource fileio_resource_; // 0 if the file is a directory. + + // Returns true if this node is a directory. + bool IsDirectory() const { + return !fileio_resource_; + } friend class MountHtml5Fs; }; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc new file mode 100644 index 0000000000..5005ff48d3 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc @@ -0,0 +1,523 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "nacl_io/mount_node_http.h" + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include <ppapi/c/pp_errors.h> + +#include "nacl_io/mount_http.h" +#include "nacl_io/osinttypes.h" + +#if defined(WIN32) +#define snprintf _snprintf +#endif + +namespace { + +// If we're attempting to read a partial request, but the server returns a full +// request, we need to read all of the data up to the start of our partial +// request into a dummy buffer. This is the maximum size of that buffer. +const size_t MAX_READ_BUFFER_SIZE = 64 * 1024; +const int32_t STATUSCODE_OK = 200; +const int32_t STATUSCODE_PARTIAL_CONTENT = 206; + +StringMap_t ParseHeaders(const char* headers, int32_t headers_length) { + enum State { + FINDING_KEY, + SKIPPING_WHITESPACE, + FINDING_VALUE, + }; + + StringMap_t result; + std::string key; + std::string value; + + State state = FINDING_KEY; + const char* start = headers; + for (int i = 0; i < headers_length; ++i) { + switch (state) { + case FINDING_KEY: + if (headers[i] == ':') { + // Found key. + key.assign(start, &headers[i] - start); + key = NormalizeHeaderKey(key); + state = SKIPPING_WHITESPACE; + } + break; + + case SKIPPING_WHITESPACE: + if (headers[i] == ' ') { + // Found whitespace, keep going... + break; + } + + // Found a non-whitespace, mark this as the start of the value. + start = &headers[i]; + state = FINDING_VALUE; + // Fallthrough to start processing value without incrementing i. + + case FINDING_VALUE: + if (headers[i] == '\n') { + // Found value. + value.assign(start, &headers[i] - start); + result[key] = value; + start = &headers[i + 1]; + state = FINDING_KEY; + } + break; + } + } + + return result; +} + +bool ParseContentLength(const StringMap_t& headers, size_t* content_length) { + StringMap_t::const_iterator iter = headers.find("Content-Length"); + if (iter == headers.end()) + return false; + + *content_length = strtoul(iter->second.c_str(), NULL, 10); + return true; +} + +bool ParseContentRange(const StringMap_t& headers, + size_t* read_start, + size_t* read_end, + size_t* entity_length) { + StringMap_t::const_iterator iter = headers.find("Content-Range"); + if (iter == headers.end()) + return false; + + // The key should look like "bytes ##-##/##" or "bytes ##-##/*". The last + // value is the entity length, which can potentially be * (i.e. unknown). + int read_start_int; + int read_end_int; + int entity_length_int; + int result = sscanf(iter->second.c_str(), + "bytes %" SCNuS "-%" SCNuS "/%" SCNuS, + &read_start_int, + &read_end_int, + &entity_length_int); + + // The Content-Range header specifies an inclusive range: e.g. the first ten + // bytes is "bytes 0-9/*". Convert it to a half-open range by incrementing + // read_end. + if (result == 2) { + *read_start = read_start_int; + *read_end = read_end_int + 1; + *entity_length = 0; + return true; + } else if (result == 3) { + *read_start = read_start_int; + *read_end = read_end_int + 1; + *entity_length = entity_length_int; + return true; + } + + return false; +} + +} // namespace + +void MountNodeHttp::SetCachedSize(off_t size) { + has_cached_size_ = true; + stat_.st_size = size; +} + +Error MountNodeHttp::FSync() { return ENOSYS; } + +Error MountNodeHttp::GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes) { + *out_bytes = 0; + return ENOSYS; +} + +Error MountNodeHttp::GetStat(struct stat* stat) { + AutoLock lock(&lock_); + + // Assume we need to 'HEAD' if we do not know the size, otherwise, assume + // that the information is constant. We can add a timeout if needed. + MountHttp* mount = static_cast<MountHttp*>(mount_); + if (stat_.st_size == 0 || !mount->cache_stat_) { + StringMap_t headers; + PP_Resource loader; + PP_Resource request; + PP_Resource response; + int32_t statuscode; + StringMap_t response_headers; + Error error = OpenUrl("HEAD", + &headers, + &loader, + &request, + &response, + &statuscode, + &response_headers); + if (error) + return error; + + ScopedResource scoped_loader(mount_->ppapi(), loader); + ScopedResource scoped_request(mount_->ppapi(), request); + ScopedResource scoped_response(mount_->ppapi(), response); + + size_t entity_length; + if (ParseContentLength(response_headers, &entity_length)) { + SetCachedSize(static_cast<off_t>(entity_length)); + } else if (cache_content_ && !has_cached_size_) { + error = DownloadToCache(); + // TODO(binji): this error should not be dropped, but it requires a bit + // of a refactor of the tests. See crbug.com/245431 + // if (error) + // return error; + } else { + // Don't use SetCachedSize here -- it is actually unknown. + stat_.st_size = 0; + } + + stat_.st_atime = 0; // TODO(binji): Use "Last-Modified". + stat_.st_mtime = 0; + stat_.st_ctime = 0; + } + + // Fill the stat structure if provided + if (stat) + memcpy(stat, &stat_, sizeof(stat_)); + + return 0; +} + +Error MountNodeHttp::Read(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + + AutoLock lock(&lock_); + if (cache_content_) { + if (cached_data_.empty()) { + Error error = DownloadToCache(); + if (error) + return error; + } + + return ReadPartialFromCache(offs, buf, count, out_bytes); + } + + return DownloadPartial(offs, buf, count, out_bytes); +} + +Error MountNodeHttp::FTruncate(off_t size) { return ENOSYS; } + +Error MountNodeHttp::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + // TODO(binji): support POST? + *out_bytes = 0; + return ENOSYS; +} + +Error MountNodeHttp::GetSize(size_t* out_size) { + *out_size = 0; + + // TODO(binji): This value should be cached properly; i.e. obey the caching + // headers returned by the server. + AutoLock lock(&lock_); + if (!has_cached_size_) { + // Even if DownloadToCache fails, the best result we can return is what + // was written to stat_.st_size. + if (cache_content_) { + Error error = DownloadToCache(); + if (error) + return error; + } + } + + *out_size = stat_.st_size; + return 0; +} + +MountNodeHttp::MountNodeHttp(Mount* mount, + const std::string& url, + bool cache_content) + : MountNode(mount), + url_(url), + cache_content_(cache_content), + has_cached_size_(false) {} + +Error MountNodeHttp::OpenUrl(const char* method, + StringMap_t* request_headers, + PP_Resource* out_loader, + PP_Resource* out_request, + PP_Resource* out_response, + int32_t* out_statuscode, + StringMap_t* out_response_headers) { + // Assume lock_ is already held. + PepperInterface* ppapi = mount_->ppapi(); + + MountHttp* mount_http = static_cast<MountHttp*>(mount_); + ScopedResource request( + ppapi, mount_http->MakeUrlRequestInfo(url_, method, request_headers)); + if (!request.pp_resource()) + return EINVAL; + + URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); + URLResponseInfoInterface* response_interface = + ppapi->GetURLResponseInfoInterface(); + VarInterface* var_interface = ppapi->GetVarInterface(); + + ScopedResource loader(ppapi, loader_interface->Create(ppapi->GetInstance())); + if (!loader.pp_resource()) + return EINVAL; + + int32_t result = loader_interface->Open( + loader.pp_resource(), request.pp_resource(), PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); + + ScopedResource response( + ppapi, loader_interface->GetResponseInfo(loader.pp_resource())); + if (!response.pp_resource()) + return EINVAL; + + // Get response statuscode. + PP_Var statuscode = response_interface->GetProperty( + response.pp_resource(), PP_URLRESPONSEPROPERTY_STATUSCODE); + + if (statuscode.type != PP_VARTYPE_INT32) + return EINVAL; + + *out_statuscode = statuscode.value.as_int; + + // Only accept OK or Partial Content. + if (*out_statuscode != STATUSCODE_OK && + *out_statuscode != STATUSCODE_PARTIAL_CONTENT) { + return EINVAL; + } + + // Get response headers. + PP_Var response_headers_var = response_interface->GetProperty( + response.pp_resource(), PP_URLRESPONSEPROPERTY_HEADERS); + + uint32_t response_headers_length; + const char* response_headers_str = + var_interface->VarToUtf8(response_headers_var, &response_headers_length); + + *out_loader = loader.Release(); + *out_request = request.Release(); + *out_response = response.Release(); + *out_response_headers = + ParseHeaders(response_headers_str, response_headers_length); + + return 0; +} + +Error MountNodeHttp::DownloadToCache() { + StringMap_t headers; + PP_Resource loader; + PP_Resource request; + PP_Resource response; + int32_t statuscode; + StringMap_t response_headers; + Error error = OpenUrl("GET", + &headers, + &loader, + &request, + &response, + &statuscode, + &response_headers); + if (error) + return error; + + PepperInterface* ppapi = mount_->ppapi(); + ScopedResource scoped_loader(ppapi, loader); + ScopedResource scoped_request(ppapi, request); + ScopedResource scoped_response(ppapi, response); + + size_t content_length = 0; + if (ParseContentLength(response_headers, &content_length)) { + cached_data_.resize(content_length); + int real_size; + error = DownloadToBuffer( + loader, cached_data_.data(), content_length, &real_size); + if (error) + return error; + + SetCachedSize(real_size); + cached_data_.resize(real_size); + return 0; + } + + // We don't know how big the file is. Read in chunks. + cached_data_.resize(MAX_READ_BUFFER_SIZE); + size_t total_bytes_read = 0; + size_t bytes_to_read = MAX_READ_BUFFER_SIZE; + while (true) { + char* buf = cached_data_.data() + total_bytes_read; + int bytes_read; + error = DownloadToBuffer(loader, buf, bytes_to_read, &bytes_read); + if (error) + return error; + + total_bytes_read += bytes_read; + + if (bytes_read < bytes_to_read) { + SetCachedSize(total_bytes_read); + cached_data_.resize(total_bytes_read); + return 0; + } + + cached_data_.resize(total_bytes_read + bytes_to_read); + } +} + +Error MountNodeHttp::ReadPartialFromCache(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + + if (offs > cached_data_.size()) + return EINVAL; + + count = std::min(count, cached_data_.size() - offs); + memcpy(buf, &cached_data_.data()[offs], count); + + *out_bytes = count; + return 0; +} + +Error MountNodeHttp::DownloadPartial(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + + StringMap_t headers; + + char buffer[100]; + // Range request is inclusive: 0-99 returns 100 bytes. + snprintf(&buffer[0], + sizeof(buffer), + "bytes=%" PRIuS "-%" PRIuS, + offs, + offs + count - 1); + headers["Range"] = buffer; + + PP_Resource loader; + PP_Resource request; + PP_Resource response; + int32_t statuscode; + StringMap_t response_headers; + Error error = OpenUrl("GET", + &headers, + &loader, + &request, + &response, + &statuscode, + &response_headers); + if (error) + return error; + + PepperInterface* ppapi = mount_->ppapi(); + ScopedResource scoped_loader(ppapi, loader); + ScopedResource scoped_request(ppapi, request); + ScopedResource scoped_response(ppapi, response); + + size_t read_start = 0; + if (statuscode == STATUSCODE_OK) { + // No partial result, read everything starting from the part we care about. + size_t content_length; + if (ParseContentLength(response_headers, &content_length)) { + if (offs >= content_length) + return EINVAL; + + // Clamp count, if trying to read past the end of the file. + if (offs + count > content_length) { + count = content_length - offs; + } + } + } else if (statuscode == STATUSCODE_PARTIAL_CONTENT) { + // Determine from the headers where we are reading. + size_t read_end; + size_t entity_length; + if (ParseContentRange( + response_headers, &read_start, &read_end, &entity_length)) { + if (read_start > offs || read_start > read_end) { + // If this error occurs, the server is returning bogus values. + return EINVAL; + } + + // Clamp count, if trying to read past the end of the file. + count = std::min(read_end - read_start, count); + } else { + // Partial Content without Content-Range. Assume that the server gave us + // exactly what we asked for. This can happen even when the server + // returns 200 -- the cache may return 206 in this case, but not modify + // the headers. + read_start = offs; + } + } + + if (read_start < offs) { + // We aren't yet at the location where we want to start reading. Read into + // our dummy buffer until then. + size_t bytes_to_read = offs - read_start; + if (buffer_.size() < bytes_to_read) + buffer_.resize(std::min(bytes_to_read, MAX_READ_BUFFER_SIZE)); + + while (bytes_to_read > 0) { + int32_t bytes_read; + Error error = + DownloadToBuffer(loader, buffer_.data(), buffer_.size(), &bytes_read); + if (error) + return error; + + bytes_to_read -= bytes_read; + } + } + + return DownloadToBuffer(loader, buf, count, out_bytes); +} + +Error MountNodeHttp::DownloadToBuffer(PP_Resource loader, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + + PepperInterface* ppapi = mount_->ppapi(); + URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); + + char* out_buffer = static_cast<char*>(buf); + size_t bytes_to_read = count; + while (bytes_to_read > 0) { + int32_t bytes_read = loader_interface->ReadResponseBody( + loader, out_buffer, bytes_to_read, PP_BlockUntilComplete()); + + if (bytes_read == 0) { + // This is not an error -- it may just be that we were trying to read + // more data than exists. + *out_bytes = count - bytes_to_read; + return 0; + } + + if (bytes_read < 0) + return PPErrorToErrno(bytes_read); + + assert(bytes_read <= bytes_to_read); + bytes_to_read -= bytes_read; + out_buffer += bytes_read; + } + + *out_bytes = count; + return 0; +} diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.h b/native_client_sdk/src/libraries/nacl_io/mount_node_http.h new file mode 100644 index 0000000000..6b174681d1 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef LIBRARIES_NACL_IO_MOUNT_NODE_HTTP_H_ +#define LIBRARIES_NACL_IO_MOUNT_NODE_HTTP_H_ + +#include <map> +#include <string> +#include <vector> + +#include "nacl_io/error.h" +#include "nacl_io/mount_node.h" +#include "nacl_io/pepper_interface.h" + +typedef std::map<std::string, std::string> StringMap_t; + +class MountNodeHttp : public MountNode { + public: + virtual Error FSync(); + virtual Error GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes); + virtual Error GetStat(struct stat* stat); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error FTruncate(off_t size); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + virtual Error GetSize(size_t* out_size); + + void SetCachedSize(off_t size); + + protected: + MountNodeHttp(Mount* mount, const std::string& url, bool cache_content); + + private: + Error OpenUrl(const char* method, + StringMap_t* request_headers, + PP_Resource* out_loader, + PP_Resource* out_request, + PP_Resource* out_response, + int32_t* out_statuscode, + StringMap_t* out_response_headers); + + Error DownloadToCache(); + Error ReadPartialFromCache(size_t offs, + void* buf, + size_t count, + int* out_bytes); + Error DownloadPartial(size_t offs, void* buf, size_t count, int* out_bytes); + Error DownloadToBuffer(PP_Resource loader, + void* buf, + size_t count, + int* out_bytes); + + std::string url_; + std::vector<char> buffer_; + + bool cache_content_; + bool has_cached_size_; + std::vector<char> cached_data_; + + friend class MountHttp; +}; + +#endif // LIBRARIES_NACL_IO_MOUNT_NODE_HTTP_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc index 0fa82baeba..d324079700 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc @@ -8,74 +8,88 @@ #include <string.h> #include "nacl_io/osstat.h" -#include "utils/auto_lock.h" +#include "sdk_util/auto_lock.h" #define BLOCK_SIZE (1 << 16) #define BLOCK_MASK (BLOCK_SIZE - 1) -MountNodeMem::MountNodeMem(Mount *mount) - : MountNode(mount), - data_(NULL), - capacity_(0) { +MountNodeMem::MountNodeMem(Mount* mount) + : MountNode(mount), data_(NULL), capacity_(0) { stat_.st_mode |= S_IFREG; } -MountNodeMem::~MountNodeMem() { - free(data_); -} +MountNodeMem::~MountNodeMem() { free(data_); } + +Error MountNodeMem::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; -int MountNodeMem::Read(size_t offs, void *buf, size_t count) { AutoLock lock(&lock_); - if (count == 0) return 0; - if (offs + count > GetSize()) { - count = GetSize() - offs; + if (count == 0) + return 0; + + size_t size = stat_.st_size; + + if (offs + count > size) { + count = size - offs; } memcpy(buf, &data_[offs], count); - return static_cast<int>(count); + *out_bytes = static_cast<int>(count); + return 0; } -int MountNodeMem::Write(size_t offs, const void *buf, size_t count) { +Error MountNodeMem::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + AutoLock lock(&lock_); - if (count == 0) return 0; + if (count == 0) + return 0; + + if (count + offs > stat_.st_size) { + Error error = FTruncate(count + offs); + if (error) + return error; - if (count + offs > GetSize()) { - FTruncate(count + offs); - count = GetSize() - offs; + count = stat_.st_size - offs; } memcpy(&data_[offs], buf, count); - return static_cast<int>(count); + *out_bytes = static_cast<int>(count); + return 0; } -int MountNodeMem::FTruncate(off_t size) { - size_t need = (size + BLOCK_MASK) & ~BLOCK_MASK; +Error MountNodeMem::FTruncate(off_t new_size) { + size_t need = (new_size + BLOCK_MASK) & ~BLOCK_MASK; + size_t old_size = stat_.st_size; // If the current capacity is correct, just adjust and return if (need == capacity_) { - stat_.st_size = static_cast<off_t>(size); + stat_.st_size = static_cast<off_t>(new_size); return 0; } // Attempt to realloc the block - char *newdata = static_cast<char *>(realloc(data_, need)); + char* newdata = static_cast<char*>(realloc(data_, need)); if (newdata != NULL) { // Zero out new space. - if (size > GetSize()) - memset(newdata + GetSize(), 0, size - GetSize()); + if (new_size > old_size) + memset(newdata + old_size, 0, new_size - old_size); data_ = newdata; capacity_ = need; - stat_.st_size = static_cast<off_t>(size); + stat_.st_size = static_cast<off_t>(new_size); return 0; } // If we failed, then adjust size according to what we keep - if (size > capacity_) size = capacity_; + if (new_size > capacity_) + new_size = capacity_; // Update the size and return the new size - stat_.st_size = static_cast<off_t>(size); - errno = EIO; - return -1; + stat_.st_size = static_cast<off_t>(new_size); + return EIO; } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h index 07acfe16d7..6457014293 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h @@ -16,9 +16,12 @@ class MountNodeMem : public MountNode { public: // Normal read/write operations on a file - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); - virtual int FTruncate(off_t size); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + virtual Error FTruncate(off_t size); private: char* data_; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc index 82660d68ea..268cbea7a5 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc @@ -9,14 +9,10 @@ class MountNodePassthrough : public MountNode { public: explicit MountNodePassthrough(Mount* mount, int real_fd) - : MountNode(mount), - real_fd_(real_fd) { - } + : MountNode(mount), real_fd_(real_fd) {} protected: - virtual bool Init(int flags) { - return true; - } + virtual Error Init(int flags) { return 0; } virtual void Destroy() { if (real_fd_) @@ -26,77 +22,74 @@ class MountNodePassthrough : public MountNode { public: // Normal read/write operations on a file - virtual int Read(size_t offs, void* buf, size_t count) { + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; + off_t new_offset; int err = _real_lseek(real_fd_, offs, 0, &new_offset); - if (err) { - errno = err; - return -1; - } + if (err) + return err; size_t nread; err = _real_read(real_fd_, buf, count, &nread); - if (err) { - errno = err; - return -1; - } + if (err) + return err; - return static_cast<int>(nread); + *out_bytes = static_cast<int>(nread); + return 0; } - virtual int Write(size_t offs, const void* buf, size_t count) { + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + off_t new_offset; int err = _real_lseek(real_fd_, offs, 0, &new_offset); - if (err) { - errno = err; - return -1; - } + if (err) + return err; size_t nwrote; err = _real_write(real_fd_, buf, count, &nwrote); - if (err) { - errno = err; - return -1; - } + if (err) + return err; - return static_cast<int>(nwrote); + *out_bytes = static_cast<int>(nwrote); + return 0; } - virtual int FTruncate(off_t size) { + virtual Error FTruncate(off_t size) { // TODO(binji): what to do here? - return -1; + return ENOSYS; } - virtual int GetDents(size_t offs, struct dirent* pdir, size_t count) { + virtual Error GetDents(size_t offs, struct dirent* pdir, size_t count) { size_t nread; int err = _real_getdents(real_fd_, pdir, count, &nread); - if (err) { - errno = err; - return -1; - } - + if (err) + return err; return nread; } - virtual int GetStat(struct stat* stat) { + virtual Error GetStat(struct stat* stat) { int err = _real_fstat(real_fd_, stat); - if (err) { - errno = err; - return -1; - } - + if (err) + return err; return 0; } - void* MMap(void* addr, size_t length, int prot, int flags, size_t offset) { - void* new_addr = addr; - int err = _real_mmap(&new_addr, length, prot, flags, real_fd_, offset); - if (err) { - errno = err; - return (void*)-1; - } - - return new_addr; + Error MMap(void* addr, + size_t length, + int prot, + int flags, + size_t offset, + void** out_addr) { + *out_addr = addr; + int err = _real_mmap(out_addr, length, prot, flags, real_fd_, offset); + if (err) + return err; + return 0; } private: @@ -105,70 +98,56 @@ class MountNodePassthrough : public MountNode { int real_fd_; }; -MountPassthrough::MountPassthrough() { -} +MountPassthrough::MountPassthrough() {} -bool MountPassthrough::Init(int dev, StringMap_t& args, - PepperInterface* ppapi) { - Mount::Init(dev, args, ppapi); - return true; +Error MountPassthrough::Init(int dev, + StringMap_t& args, + PepperInterface* ppapi) { + return Mount::Init(dev, args, ppapi); } -void MountPassthrough::Destroy() { -} +void MountPassthrough::Destroy() {} + +Error MountPassthrough::Open(const Path& path, int mode, MountNode** out_node) { + *out_node = NULL; -MountNode *MountPassthrough::Open(const Path& path, int mode) { int real_fd; - int err = _real_open(path.Join().c_str(), mode, 0666, &real_fd); - if (err) { - errno = err; - return NULL; - } + int error = _real_open(path.Join().c_str(), mode, 0666, &real_fd); + if (error) + return error; MountNodePassthrough* node = new MountNodePassthrough(this, real_fd); - return node; + *out_node = node; + return 0; } -MountNode *MountPassthrough::OpenResource(const Path& path) { +Error MountPassthrough::OpenResource(const Path& path, MountNode** out_node) { + *out_node = NULL; + int real_fd; - int err = _real_open_resource(path.Join().c_str(), &real_fd); - if (err) { - errno = err; - return NULL; - } + int error = _real_open_resource(path.Join().c_str(), &real_fd); + if (error) + return error; MountNodePassthrough* node = new MountNodePassthrough(this, real_fd); - return node; + *out_node = node; + return 0; } -int MountPassthrough::Unlink(const Path& path) { +Error MountPassthrough::Unlink(const Path& path) { // Not implemented by NaCl. - errno = ENOSYS; - return -1; + return ENOSYS; } -int MountPassthrough::Mkdir(const Path& path, int perm) { - int err = _real_mkdir(path.Join().c_str(), perm); - if (err) { - errno = err; - return -1; - } - - return 0; +Error MountPassthrough::Mkdir(const Path& path, int perm) { + return _real_mkdir(path.Join().c_str(), perm); } -int MountPassthrough::Rmdir(const Path& path) { - int err = _real_rmdir(path.Join().c_str()); - if (err) { - errno = err; - return -1; - } - - return 0; +Error MountPassthrough::Rmdir(const Path& path) { + return _real_rmdir(path.Join().c_str()); } -int MountPassthrough::Remove(const Path& path) { +Error MountPassthrough::Remove(const Path& path) { // Not implemented by NaCl. - errno = ENOSYS; - return -1; + return ENOSYS; } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h index 00a0eec98f..b143abf007 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h @@ -11,16 +11,16 @@ class MountPassthrough : public Mount { protected: MountPassthrough(); - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); public: - virtual MountNode *Open(const Path& path, int mode); - virtual MountNode *OpenResource(const Path& path); - virtual int Unlink(const Path& path); - virtual int Mkdir(const Path& path, int perm); - virtual int Rmdir(const Path& path); - virtual int Remove(const Path& path); + virtual Error Open(const Path& path, int mode, MountNode** out_node); + virtual Error OpenResource(const Path& path, MountNode** out_node); + virtual Error Unlink(const Path& path); + virtual Error Mkdir(const Path& path, int perm); + virtual Error Rmdir(const Path& path); + virtual Error Remove(const Path& path); private: friend class Mount; diff --git a/native_client_sdk/src/libraries/nacl_io/nacl_io.h b/native_client_sdk/src/libraries/nacl_io/nacl_io.h index f9343538ca..b408bb9385 100644 --- a/native_client_sdk/src/libraries/nacl_io/nacl_io.h +++ b/native_client_sdk/src/libraries/nacl_io/nacl_io.h @@ -9,7 +9,7 @@ #include <ppapi/c/ppb.h> #include "nacl_io/kernel_wrap.h" -#include "utils/macros.h" +#include "sdk_util/macros.h" EXTERN_C_BEGIN diff --git a/native_client_sdk/src/libraries/nacl_io/nacl_mounts.vcproj b/native_client_sdk/src/libraries/nacl_io/nacl_mounts.vcproj index 875a16f99f..d57c84fbaa 100644 --- a/native_client_sdk/src/libraries/nacl_io/nacl_mounts.vcproj +++ b/native_client_sdk/src/libraries/nacl_io/nacl_mounts.vcproj @@ -41,7 +41,7 @@ <Tool Name="VCCLCompilerTool" Optimization="0" - AdditionalIncludeDirectories="../win;../utils" + AdditionalIncludeDirectories="../win;../sdk_util" PreprocessorDefinitions="WIN32;_DEBUG;_LIB" MinimalRebuild="true" BasicRuntimeChecks="3" diff --git a/native_client_sdk/src/libraries/nacl_io/osdirent.h b/native_client_sdk/src/libraries/nacl_io/osdirent.h index 22dcd616f9..39a3b957bb 100644 --- a/native_client_sdk/src/libraries/nacl_io/osdirent.h +++ b/native_client_sdk/src/libraries/nacl_io/osdirent.h @@ -8,7 +8,7 @@ #if defined(WIN32) #include <sys/types.h> -#include "utils/macros.h" +#include "sdk_util/macros.h" struct dirent { _ino_t d_ino; diff --git a/native_client_sdk/src/libraries/nacl_io/path.h b/native_client_sdk/src/libraries/nacl_io/path.h index 7ac41f2f25..fcbaa33d67 100644 --- a/native_client_sdk/src/libraries/nacl_io/path.h +++ b/native_client_sdk/src/libraries/nacl_io/path.h @@ -8,7 +8,7 @@ #include <string> #include <vector> -#include "utils/macros.h" +#include "sdk_util/macros.h" typedef std::vector<std::string> StringArray_t; diff --git a/native_client_sdk/src/libraries/nacl_io/pepper/all_interfaces.h b/native_client_sdk/src/libraries/nacl_io/pepper/all_interfaces.h index 679573607d..5bab3b23c4 100644 --- a/native_client_sdk/src/libraries/nacl_io/pepper/all_interfaces.h +++ b/native_client_sdk/src/libraries/nacl_io/pepper/all_interfaces.h @@ -39,12 +39,14 @@ BEGIN_INTERFACE(FileIoInterface, PPB_FileIO, PPB_FILEIO_INTERFACE_1_0) const char*, int32_t, PP_CompletionCallback) END_INTERFACE(FileIoInterface, PPB_FileIO) -BEGIN_INTERFACE(FileRefInterface, PPB_FileRef, PPB_FILEREF_INTERFACE_1_0) +BEGIN_INTERFACE(FileRefInterface, PPB_FileRef, PPB_FILEREF_INTERFACE_1_1) METHOD2(FileRefInterface, PP_Resource, Create, PP_Resource, const char*) METHOD2(FileRefInterface, int32_t, Delete, PP_Resource, PP_CompletionCallback) METHOD1(FileRefInterface, PP_Var, GetName, PP_Resource) METHOD3(FileRefInterface, int32_t, MakeDirectory, PP_Resource, PP_Bool, PP_CompletionCallback) + METHOD3(FileRefInterface, int32_t, Query, PP_Resource, PP_FileInfo*, + PP_CompletionCallback) METHOD3(FileRefInterface, int32_t, ReadDirectoryEntries, PP_Resource, const PP_ArrayOutput&, PP_CompletionCallback) END_INTERFACE(FileRefInterface, PPB_FileRef) diff --git a/native_client_sdk/src/libraries/nacl_io/pepper_interface.h b/native_client_sdk/src/libraries/nacl_io/pepper_interface.h index 512fd6fdbf..31915a4e76 100644 --- a/native_client_sdk/src/libraries/nacl_io/pepper_interface.h +++ b/native_client_sdk/src/libraries/nacl_io/pepper_interface.h @@ -21,7 +21,7 @@ #include <ppapi/c/ppb_url_response_info.h> #include <ppapi/c/ppb_var.h> -#include <utils/macros.h> +#include <sdk_util/macros.h> // Note: To add a new interface: // diff --git a/native_client_sdk/src/libraries/nacl_io_test/example.dsc b/native_client_sdk/src/libraries/nacl_io_test/example.dsc index a47ed63744..df934a56a2 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/example.dsc +++ b/native_client_sdk/src/libraries/nacl_io_test/example.dsc @@ -1,5 +1,4 @@ { - # TODO(binji): pnacl doesn't build right now because gtest doesn't build yet. 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win'], # Need to add ../../examples for common.js diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc index 268c6f51a6..9e6f4832a7 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc @@ -23,22 +23,21 @@ namespace { class MountRefMock : public Mount { public: MountRefMock(int* mount_count, int* handle_count) - : mount_count(mount_count), - handle_count(handle_count) { + : mount_count(mount_count), handle_count(handle_count) { (*mount_count)++; } - ~MountRefMock() { - (*mount_count)--; - } + ~MountRefMock() { (*mount_count)--; } public: - MountNode* Open(const Path& path, int mode) { return NULL; } - int Close(MountNode* node) { return 0; } - int Unlink(const Path& path) { return 0; } - int Mkdir(const Path& path, int permissions) { return 0; } - int Rmdir(const Path& path) { return 0; } - int Remove(const Path& path) { return 0; } + Error Open(const Path& path, int mode, MountNode** out_node) { + *out_node = NULL; + return ENOSYS; + } + Error Unlink(const Path& path) { return 0; } + Error Mkdir(const Path& path, int permissions) { return 0; } + Error Rmdir(const Path& path) { return 0; } + Error Remove(const Path& path) { return 0; } public: int* mount_count; @@ -47,8 +46,8 @@ class MountRefMock : public Mount { class KernelHandleRefMock : public KernelHandle { public: - KernelHandleRefMock(Mount* mnt, MountNode* node, int flags) - : KernelHandle(mnt, node, flags) { + KernelHandleRefMock(Mount* mnt, MountNode* node) + : KernelHandle(mnt, node) { MountRefMock* mock_mount = static_cast<MountRefMock*>(mnt); (*mock_mount->handle_count)++; } @@ -61,9 +60,7 @@ class KernelHandleRefMock : public KernelHandle { class KernelObjectTest : public ::testing::Test { public: - KernelObjectTest() - : mount_count(0), - handle_count(0) { + KernelObjectTest() : mount_count(0), handle_count(0) { proxy = new KernelObject; mnt = new MountRefMock(&mount_count, &handle_count); } @@ -81,10 +78,10 @@ class KernelObjectTest : public ::testing::Test { } // namespace - TEST_F(KernelObjectTest, Referencing) { - KernelHandle* handle = new KernelHandleRefMock(mnt, NULL, 0); - KernelHandle* handle2 = new KernelHandleRefMock(mnt, NULL, 0); + KernelHandle* handle = new KernelHandleRefMock(mnt, NULL); + KernelHandle* handle2 = new KernelHandleRefMock(mnt, NULL); + KernelHandle* result_handle = NULL; // Objects should have one ref when we start EXPECT_EQ(1, mnt->RefCount()); @@ -111,14 +108,16 @@ TEST_F(KernelObjectTest, Referencing) { EXPECT_EQ(2, fd3); // We should find the handle by either fd - EXPECT_EQ(handle, proxy->AcquireHandle(fd1)); - EXPECT_EQ(handle, proxy->AcquireHandle(fd2)); + EXPECT_EQ(0, proxy->AcquireHandle(fd1, &result_handle)); + EXPECT_EQ(handle, result_handle); + EXPECT_EQ(0, proxy->AcquireHandle(fd2, &result_handle)); + EXPECT_EQ(handle, result_handle); // A non existent fd should fail - EXPECT_EQ(NULL, proxy->AcquireHandle(-1)); - EXPECT_EQ(EBADF, errno); - EXPECT_EQ(NULL, proxy->AcquireHandle(100)); - EXPECT_EQ(EBADF, errno); + EXPECT_EQ(EBADF, proxy->AcquireHandle(-1, &result_handle)); + EXPECT_EQ(NULL, result_handle); + EXPECT_EQ(EBADF, proxy->AcquireHandle(100, &result_handle)); + EXPECT_EQ(NULL, result_handle); // Acquiring the handle, should have ref'd it EXPECT_EQ(4, mnt->RefCount()); @@ -155,27 +154,35 @@ TEST_F(KernelObjectTest, Referencing) { } TEST_F(KernelObjectTest, FreeAndReassignFD) { + KernelHandle* result_handle = NULL; + EXPECT_EQ(0, handle_count); - KernelHandle* handle = new KernelHandleRefMock(mnt, NULL, 0); + KernelHandle* handle = new KernelHandleRefMock(mnt, NULL); EXPECT_EQ(1, handle_count); EXPECT_EQ(1, handle->RefCount()); // Assign to a non-existent FD proxy->FreeAndReassignFD(2, handle); - EXPECT_EQ((KernelHandle*)NULL, proxy->AcquireHandle(0)); - EXPECT_EQ((KernelHandle*)NULL, proxy->AcquireHandle(1)); - EXPECT_EQ(handle, proxy->AcquireHandle(2)); + EXPECT_EQ(EBADF, proxy->AcquireHandle(0, &result_handle)); + EXPECT_EQ(NULL, result_handle); + EXPECT_EQ(EBADF, proxy->AcquireHandle(1, &result_handle)); + EXPECT_EQ(NULL, result_handle); + EXPECT_EQ(0, proxy->AcquireHandle(2, &result_handle)); + EXPECT_EQ(handle, result_handle); proxy->ReleaseHandle(handle); EXPECT_EQ(1, handle_count); EXPECT_EQ(2, handle->RefCount()); proxy->FreeAndReassignFD(0, handle); - EXPECT_EQ(handle, proxy->AcquireHandle(0)); - EXPECT_EQ((KernelHandle*)NULL, proxy->AcquireHandle(1)); - EXPECT_EQ(handle, proxy->AcquireHandle(2)); + EXPECT_EQ(0, proxy->AcquireHandle(0, &result_handle)); + EXPECT_EQ(handle, result_handle); + EXPECT_EQ(EBADF, proxy->AcquireHandle(1, &result_handle)); + EXPECT_EQ(NULL, result_handle); + EXPECT_EQ(0, proxy->AcquireHandle(2, &result_handle)); + EXPECT_EQ(handle, result_handle); proxy->ReleaseHandle(handle); proxy->ReleaseHandle(handle); diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc index d6816535e4..40f4ef7a15 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc @@ -21,11 +21,9 @@ #include "gtest/gtest.h" - class KernelProxyTest : public ::testing::Test { public: - KernelProxyTest() - : kp_(new KernelProxy) { + KernelProxyTest() : kp_(new KernelProxy) { ki_init(kp_); // Unmount the passthrough FS and mount a memfs. EXPECT_EQ(0, kp_->umount("/")); @@ -41,7 +39,6 @@ class KernelProxyTest : public ::testing::Test { KernelProxy* kp_; }; - TEST_F(KernelProxyTest, WorkingDirectory) { char text[1024]; @@ -50,7 +47,7 @@ TEST_F(KernelProxyTest, WorkingDirectory) { EXPECT_STREQ("/", text); char* alloc = ki_getwd(NULL); - EXPECT_EQ((char *) NULL, alloc); + EXPECT_EQ((char*)NULL, alloc); EXPECT_EQ(EFAULT, errno); text[0] = 0; @@ -113,7 +110,8 @@ TEST_F(KernelProxyTest, MemMountIO) { EXPECT_NE(-1, fd3); len = ki_read(fd3, text, sizeof(text)); - if (len > -0) text[len] = 0; + if (len > 0) + text[len] = 0; EXPECT_EQ(5, len); EXPECT_STREQ("HELLO", text); EXPECT_EQ(0, ki_close(fd1)); @@ -124,7 +122,8 @@ TEST_F(KernelProxyTest, MemMountIO) { EXPECT_EQ(5, ki_write(fd1, "WORLD", 5)); len = ki_read(fd3, text, sizeof(text)); - if (len >= 0) text[len] = 0; + if (len >= 0) + text[len] = 0; EXPECT_EQ(5, len); EXPECT_STREQ("WORLD", text); @@ -132,7 +131,8 @@ TEST_F(KernelProxyTest, MemMountIO) { fd2 = ki_open("/foo/bar", O_RDONLY); EXPECT_NE(-1, fd2); len = ki_read(fd2, text, sizeof(text)); - if (len > 0) text[len] = 0; + if (len > 0) + text[len] = 0; EXPECT_EQ(10, len); EXPECT_STREQ("HELLOWORLD", text); } @@ -197,17 +197,17 @@ TEST_F(KernelProxyTest, MemMountDup) { // fd, new_fd, dup_fd -> "/bar" } - StringMap_t g_StringMap; class MountMockInit : public MountMem { public: - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi) { + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi) { g_StringMap = args; if (args.find("false") != args.end()) - return false; - return true; - }; + return EINVAL; + return 0; + } + ; }; class KernelProxyMountMock : public KernelProxy { @@ -219,10 +219,7 @@ class KernelProxyMountMock : public KernelProxy { class KernelProxyMountTest : public ::testing::Test { public: - KernelProxyMountTest() - : kp_(new KernelProxyMountMock) { - ki_init(kp_); - } + KernelProxyMountTest() : kp_(new KernelProxyMountMock) { ki_init(kp_); } ~KernelProxyMountTest() { ki_uninit(); @@ -245,28 +242,38 @@ TEST_F(KernelProxyMountTest, MountInit) { EXPECT_EQ("y", g_StringMap["x"]); } - namespace { int g_MMapCount = 0; class MountNodeMockMMap : public MountNode { public: - MountNodeMockMMap(Mount* mount) - : MountNode(mount), - node_mmap_count_(0) { - Init(0); + MountNodeMockMMap(Mount* mount) : MountNode(mount), node_mmap_count_(0) { + EXPECT_EQ(0, Init(0)); } - virtual void* MMap(void* addr, size_t length, int prot, int flags, - size_t offset) { + virtual Error MMap(void* addr, + size_t length, + int prot, + int flags, + size_t offset, + void** out_addr) { node_mmap_count_++; switch (g_MMapCount++) { - case 0: return reinterpret_cast<void*>(0x1000); - case 1: return reinterpret_cast<void*>(0x2000); - case 2: return reinterpret_cast<void*>(0x3000); - default: return MAP_FAILED; + case 0: + *out_addr = reinterpret_cast<void*>(0x1000); + break; + case 1: + *out_addr = reinterpret_cast<void*>(0x2000); + break; + case 2: + *out_addr = reinterpret_cast<void*>(0x3000); + break; + default: + return EPERM; } + + return 0; } private: @@ -275,16 +282,20 @@ class MountNodeMockMMap : public MountNode { class MountMockMMap : public Mount { public: - virtual MountNode* Open(const Path& path, int mode) { + virtual Error Open(const Path& path, int mode, MountNode** out_node) { MountNodeMockMMap* node = new MountNodeMockMMap(this); - return node; + *out_node = node; + return 0; } - virtual MountNode* OpenResource(const Path& path) { return NULL; } - virtual int Unlink(const Path& path) { return -1; } - virtual int Mkdir(const Path& path, int permissions) { return -1; } - virtual int Rmdir(const Path& path) { return -1; } - virtual int Remove(const Path& path) { return -1; } + virtual Error OpenResource(const Path& path, MountNode** out_node) { + *out_node = NULL; + return ENOSYS; + } + virtual Error Unlink(const Path& path) { return ENOSYS; } + virtual Error Mkdir(const Path& path, int permissions) { return ENOSYS; } + virtual Error Rmdir(const Path& path) { return ENOSYS; } + virtual Error Remove(const Path& path) { return ENOSYS; } }; class KernelProxyMockMMap : public KernelProxy { @@ -296,10 +307,7 @@ class KernelProxyMockMMap : public KernelProxy { class KernelProxyMMapTest : public ::testing::Test { public: - KernelProxyMMapTest() - : kp_(new KernelProxyMockMMap) { - ki_init(kp_); - } + KernelProxyMMapTest() : kp_(new KernelProxyMockMMap) { ki_init(kp_); } ~KernelProxyMMapTest() { ki_uninit(); diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc index f6f010c84d..34d8472ff2 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc @@ -81,6 +81,10 @@ class KernelWrapTest : public ::testing::Test { .WillOnce(Return(0)) .WillOnce(Return(1)) .WillOnce(Return(2)); + // And will call mount / and /dev. + EXPECT_CALL(mock, mount(_, _, _, _, _)) + .WillOnce(Return(0)) + .WillOnce(Return(0)); ki_init(&mock); } diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc index ce3ab2d3c9..e92a20aa5d 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc @@ -3,6 +3,7 @@ * found in the LICENSE file. */ +#include <errno.h> #include <fcntl.h> #include <string.h> #include <gmock/gmock.h> @@ -93,7 +94,7 @@ class MountHtml5FsNodeTest : public MountHtml5FsTest { virtual void SetUp(); virtual void TearDown(); - void SetUpNodeExpectations(); + void SetUpNodeExpectations(PP_FileType file_type); void InitFilesystem(); void InitNode(); @@ -131,22 +132,32 @@ void MountHtml5FsNodeTest::TearDown() { } } -void MountHtml5FsNodeTest::SetUpNodeExpectations() { +void MountHtml5FsNodeTest::SetUpNodeExpectations(PP_FileType file_type) { // Open. EXPECT_CALL(*fileref_, Create(filesystem_resource_, StrEq(&path_[0]))) .WillOnce(Return(fileref_resource_)); - EXPECT_CALL(*fileio_, Create(instance_)).WillOnce(Return(fileio_resource_)); - int32_t open_flags = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE | - PP_FILEOPENFLAG_CREATE; - EXPECT_CALL(*fileio_, - Open(fileio_resource_, fileref_resource_, open_flags, _)) - .WillOnce(Return(int32_t(PP_OK))); + PP_FileInfo info; + memset(&info, 0, sizeof(PP_FileInfo)); + info.type = file_type; + EXPECT_CALL(*fileref_, Query(fileref_resource_, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(info), + Return(int32_t(PP_OK)))); + if (file_type != PP_FILETYPE_DIRECTORY) { + EXPECT_CALL(*fileio_, Create(instance_)).WillOnce(Return(fileio_resource_)); + int32_t open_flags = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE | + PP_FILEOPENFLAG_CREATE; + EXPECT_CALL(*fileio_, + Open(fileio_resource_, fileref_resource_, open_flags, _)) + .WillOnce(Return(int32_t(PP_OK))); + + // Close. + EXPECT_CALL(*fileio_, Close(fileio_resource_)); + EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource_)); + EXPECT_CALL(*fileio_, Flush(fileio_resource_, _)); + } // Close. - EXPECT_CALL(*fileio_, Close(fileio_resource_)); EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource_)); - EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource_)); - EXPECT_CALL(*fileio_, Flush(fileio_resource_, _)); } void MountHtml5FsNodeTest::InitFilesystem() { @@ -155,7 +166,7 @@ void MountHtml5FsNodeTest::InitFilesystem() { } void MountHtml5FsNodeTest::InitNode() { - node_ = mnt_->Open(Path(path_), O_CREAT | O_RDWR); + ASSERT_EQ(0, mnt_->Open(Path(path_), O_CREAT | O_RDWR, &node_)); ASSERT_NE((MountNode*)NULL, node_); } @@ -163,17 +174,34 @@ void MountHtml5FsNodeTest::InitNode() { // creation of the mount blocks until the filesystem is ready. class MountHtml5FsNodeSyncTest : public MountHtml5FsNodeTest { public: + void SetUpForFileType(PP_FileType file_type); + virtual void SetUp(); }; -void MountHtml5FsNodeSyncTest::SetUp() { +void MountHtml5FsNodeSyncTest::SetUpForFileType(PP_FileType file_type) { MountHtml5FsNodeTest::SetUp(); SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); InitFilesystem(); - SetUpNodeExpectations(); + SetUpNodeExpectations(file_type); InitNode(); } +void MountHtml5FsNodeSyncTest::SetUp() { + SetUpForFileType(PP_FILETYPE_REGULAR); +} + +// Node test where the filesystem is opened synchronously, and the node is a +// directory. +class MountHtml5FsNodeSyncDirTest : public MountHtml5FsNodeSyncTest { + public: + virtual void SetUp(); +}; + +void MountHtml5FsNodeSyncDirTest::SetUp() { + SetUpForFileType(PP_FILETYPE_DIRECTORY); +} + void ReadDirectoryEntriesAction(const PP_ArrayOutput& output) { const int fileref_resource_1 = 238; const int fileref_resource_2 = 239; @@ -224,7 +252,7 @@ void MountHtml5FsNodeAsyncTest::SetUp() { // true => asynchronous filesystem open. SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0, true); InitFilesystem(); - SetUpNodeExpectations(); + SetUpNodeExpectations(PP_FILETYPE_REGULAR); // Signal the other thread to try opening a Node. pthread_mutex_lock(&mutex_); @@ -350,7 +378,8 @@ TEST_F(MountHtml5FsNodeSyncTest, Write) { EXPECT_CALL(*fileio_, Write(fileio_resource_, offset, &buffer[0], count, _)) .WillOnce(Return(count)); - int result = node_->Write(offset, &buffer, count); + int result = 0; + EXPECT_EQ(0, node_->Write(offset, &buffer, count, &result)); EXPECT_EQ(count, result); } @@ -362,7 +391,8 @@ TEST_F(MountHtml5FsNodeSyncTest, Read) { EXPECT_CALL(*fileio_, Read(fileio_resource_, offset, &buffer[0], count, _)) .WillOnce(Return(count)); - int result = node_->Read(offset, &buffer, count); + int result = 0; + EXPECT_EQ(0, node_->Read(offset, &buffer, count, &result)); EXPECT_EQ(count, result); } @@ -380,7 +410,7 @@ TEST_F(MountHtml5FsNodeSyncTest, GetStat) { info.last_access_time = access_time; info.last_modified_time = modified_time; - EXPECT_CALL(*fileio_, Query(fileio_resource_, _, _)) + EXPECT_CALL(*fileref_, Query(fileref_resource_, _, _)) .WillOnce(DoAll(SetArgPointee<1>(info), Return(int32_t(PP_OK)))); @@ -405,6 +435,76 @@ TEST_F(MountHtml5FsNodeSyncTest, FTruncate) { } TEST_F(MountHtml5FsNodeSyncTest, GetDents) { + struct dirent dirents[2]; + memset(&dirents[0], 0, sizeof(dirents)); + + // Should fail for regular files. + int result_bytes = 0; + EXPECT_EQ(ENOTDIR, node_->GetDents(0, &dirents[0], sizeof(dirent) * 2, + &result_bytes)); + ASSERT_EQ(0, result_bytes); +} + +TEST_F(MountHtml5FsNodeSyncDirTest, OpenAndClose) { +} + +TEST_F(MountHtml5FsNodeSyncDirTest, Write) { + const int offset = 10; + const int count = 20; + const char buffer[30] = {0}; + + // Should fail for directories. + int result_bytes = 0; + EXPECT_EQ(EISDIR, node_->Write(offset, &buffer, count, &result_bytes)); + ASSERT_EQ(0, result_bytes); +} + +TEST_F(MountHtml5FsNodeSyncDirTest, Read) { + const int offset = 10; + const int count = 20; + char buffer[30] = {0}; + + // Should fail for directories. + int result_bytes = 0; + EXPECT_EQ(EISDIR, node_->Read(offset, &buffer, count, &result_bytes)); + ASSERT_EQ(0, result_bytes); +} + +TEST_F(MountHtml5FsNodeSyncDirTest, GetStat) { + const int creation_time = 1000; + const int access_time = 2000; + const int modified_time = 3000; + + PP_FileInfo info; + info.size = 0; + info.type = PP_FILETYPE_DIRECTORY; + info.system_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT; + info.creation_time = creation_time; + info.last_access_time = access_time; + info.last_modified_time = modified_time; + + EXPECT_CALL(*fileref_, Query(fileref_resource_, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(info), + Return(int32_t(PP_OK)))); + + struct stat statbuf; + int result = node_->GetStat(&statbuf); + + EXPECT_EQ(0, result); + EXPECT_EQ(S_IFDIR | S_IWRITE | S_IREAD, statbuf.st_mode); + EXPECT_EQ(0, statbuf.st_size); + EXPECT_EQ(access_time, statbuf.st_atime); + EXPECT_EQ(modified_time, statbuf.st_mtime); + EXPECT_EQ(creation_time, statbuf.st_ctime); +} + +TEST_F(MountHtml5FsNodeSyncDirTest, FTruncate) { + const int size = 123; + // Should fail for directories. + EXPECT_EQ(EISDIR, node_->FTruncate(size)); +} + +TEST_F(MountHtml5FsNodeSyncDirTest, GetDents) { const int fileref_resource_1 = 238; const int fileref_resource_2 = 239; @@ -441,9 +541,20 @@ TEST_F(MountHtml5FsNodeSyncTest, GetDents) { struct dirent dirents[2]; memset(&dirents[0], 0, sizeof(dirents)); - int result = node_->GetDents(0, &dirents[0], sizeof(dirent) * 2); - - EXPECT_EQ(0, result); - EXPECT_STREQ(&fileref_name_cstr_1[0], &dirents[0].d_name[0]); - EXPECT_STREQ(&fileref_name_cstr_2[0], &dirents[1].d_name[0]); + // +2 to test a size that is not a multiple of sizeof(dirent). + // Expect it to round down. + int result_bytes = 0; + EXPECT_EQ( + 0, + node_->GetDents(0, &dirents[0], sizeof(dirent) * 2 + 2, &result_bytes)); + + ASSERT_EQ(sizeof(dirent) * 2, result_bytes); + EXPECT_LT(0, dirents[0].d_ino); // 0 is an invalid inode number. + EXPECT_EQ(sizeof(dirent), dirents[0].d_off); + EXPECT_EQ(sizeof(dirent), dirents[0].d_reclen); + EXPECT_STREQ(fileref_name_cstr_1, dirents[0].d_name); + EXPECT_LT(0, dirents[1].d_ino); // 0 is an invalid inode number. + EXPECT_EQ(sizeof(dirent), dirents[1].d_off); + EXPECT_EQ(sizeof(dirent), dirents[1].d_reclen); + EXPECT_STREQ(fileref_name_cstr_2, dirents[1].d_name); } diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc index 350874d34d..ced8d06a6c 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc @@ -29,7 +29,7 @@ using ::testing::StrEq; class MountHttpMock : public MountHttp { public: MountHttpMock(StringMap_t map, PepperInterfaceMock* ppapi) { - EXPECT_TRUE(Init(1, map, ppapi)); + EXPECT_EQ(0, Init(1, map, ppapi)); } ~MountHttpMock() { @@ -71,25 +71,34 @@ TEST_F(MountHttpTest, MountEmpty) { TEST_F(MountHttpTest, ParseManifest) { StringMap_t args; + size_t result_size = 0; + mnt_ = new MountHttpMock(args, &ppapi_); char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; - EXPECT_TRUE(mnt_->ParseManifest(manifest)); + EXPECT_EQ(0, mnt_->ParseManifest(manifest)); - MountNodeDir* root = mnt_->FindOrCreateDir(Path("/")); + MountNodeDir* root = NULL; + EXPECT_EQ(0, mnt_->FindOrCreateDir(Path("/"), &root)); + ASSERT_NE((MountNode*)NULL, root); EXPECT_EQ(2, root->ChildCount()); - MountNodeDir* dir = mnt_->FindOrCreateDir(Path("/mydir")); + MountNodeDir* dir = NULL; + EXPECT_EQ(0, mnt_->FindOrCreateDir(Path("/mydir"), &dir)); + ASSERT_NE((MountNode*)NULL, dir); EXPECT_EQ(1, dir->ChildCount()); MountNode* node = mnt_->GetMap()["/mydir/foo"]; - EXPECT_TRUE(node); - EXPECT_EQ(123, node->GetSize()); + EXPECT_NE((MountNode*)NULL, node); + EXPECT_EQ(0, node->GetSize(&result_size)); + EXPECT_EQ(123, result_size); // Since these files are cached thanks to the manifest, we can open them // without accessing the PPAPI URL API. - MountNode* foo = mnt_->Open(Path("/mydir/foo"), O_RDONLY); - MountNode* bar = mnt_->Open(Path("/thatdir/bar"), O_RDWR); + MountNode* foo = NULL; + EXPECT_EQ(0, mnt_->Open(Path("/mydir/foo"), O_RDONLY, &foo)); + MountNode* bar = NULL; + EXPECT_EQ(0, mnt_->Open(Path("/thatdir/bar"), O_RDWR, &bar)); struct stat sfoo; struct stat sbar; @@ -248,7 +257,7 @@ void MountHttpNodeTest::SetResponseBody(const char* body) { } void MountHttpNodeTest::OpenNode() { - node_ = mnt_->Open(Path(path_), O_RDONLY); + ASSERT_EQ(0, mnt_->Open(Path(path_), O_RDONLY, &node_)); ASSERT_NE((MountNode*)NULL, node_); } @@ -266,7 +275,9 @@ void MountHttpNodeTest::TearDown() { delete mnt_; } -TEST_F(MountHttpNodeTest, OpenAndClose) { +TEST_F(MountHttpNodeTest, OpenAndCloseNoCache) { + StringMap_t smap; + smap["cache_content"] = "false"; SetMountArgs(StringMap_t()); ExpectOpen("HEAD"); ExpectHeaders(""); @@ -275,6 +286,9 @@ TEST_F(MountHttpNodeTest, OpenAndClose) { } TEST_F(MountHttpNodeTest, ReadCached) { + size_t result_size = 0; + int result_bytes = 0; + SetMountArgs(StringMap_t()); ExpectOpen("HEAD"); ExpectHeaders(""); @@ -282,7 +296,8 @@ TEST_F(MountHttpNodeTest, ReadCached) { OpenNode(); ResetMocks(); - EXPECT_EQ(42, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(42, result_size); char buf[10]; memset(&buf[0], 0, sizeof(buf)); @@ -291,20 +306,24 @@ TEST_F(MountHttpNodeTest, ReadCached) { ExpectHeaders(""); SetResponse(200, "Content-Length: 42\n"); SetResponseBody("Here is some response text. And some more."); - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("Here is s", &buf[0]); ResetMocks(); // Further reads should be cached. - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("Here is s", &buf[0]); - node_->Read(10, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(10, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("me respon", &buf[0]); - EXPECT_EQ(42, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(42, result_size); } TEST_F(MountHttpNodeTest, ReadCachedNoContentLength) { + size_t result_size = 0; + int result_bytes = 0; + SetMountArgs(StringMap_t()); ExpectOpen("HEAD"); ExpectHeaders(""); @@ -319,25 +338,30 @@ TEST_F(MountHttpNodeTest, ReadCachedNoContentLength) { // GetSize will Read() because it didn't get the content length from the HEAD // request. - EXPECT_EQ(42, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(42, result_size); char buf[10]; memset(&buf[0], 0, sizeof(buf)); - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("Here is s", &buf[0]); ResetMocks(); // Further reads should be cached. - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("Here is s", &buf[0]); - node_->Read(10, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(10, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("me respon", &buf[0]); - EXPECT_EQ(42, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(42, result_size); } TEST_F(MountHttpNodeTest, ReadCachedUnderrun) { + size_t result_size = 0; + int result_bytes = 0; + SetMountArgs(StringMap_t()); ExpectOpen("HEAD"); ExpectHeaders(""); @@ -345,7 +369,8 @@ TEST_F(MountHttpNodeTest, ReadCachedUnderrun) { OpenNode(); ResetMocks(); - EXPECT_EQ(100, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(100, result_size); char buf[10]; memset(&buf[0], 0, sizeof(buf)); @@ -354,14 +379,19 @@ TEST_F(MountHttpNodeTest, ReadCachedUnderrun) { ExpectHeaders(""); SetResponse(200, "Content-Length: 100\n"); SetResponseBody("abcdefghijklmnopqrstuvwxyz"); - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("abcdefghi", &buf[0]); ResetMocks(); - EXPECT_EQ(26, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(26, result_size); } TEST_F(MountHttpNodeTest, ReadCachedOverrun) { + size_t result_size = 0; + int result_bytes = 0; + SetMountArgs(StringMap_t()); ExpectOpen("HEAD"); ExpectHeaders(""); @@ -369,7 +399,8 @@ TEST_F(MountHttpNodeTest, ReadCachedOverrun) { OpenNode(); ResetMocks(); - EXPECT_EQ(15, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(15, result_size); char buf[10]; memset(&buf[0], 0, sizeof(buf)); @@ -378,14 +409,18 @@ TEST_F(MountHttpNodeTest, ReadCachedOverrun) { ExpectHeaders(""); SetResponse(200, "Content-Length: 15\n"); SetResponseBody("01234567890123456789"); - node_->Read(10, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(10, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(5, result_bytes); EXPECT_STREQ("01234", &buf[0]); ResetMocks(); - EXPECT_EQ(15, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(15, result_size); } TEST_F(MountHttpNodeTest, ReadPartial) { + int result_bytes = 0; + StringMap_t args; args["cache_content"] = "false"; SetMountArgs(args); @@ -402,7 +437,8 @@ TEST_F(MountHttpNodeTest, ReadPartial) { ExpectHeaders("Range: bytes=0-8\n"); SetResponse(206, "Content-Length: 9\nContent-Range: bytes=0-8\n"); SetResponseBody("012345678"); - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("012345678", &buf[0]); ResetMocks(); @@ -411,11 +447,14 @@ TEST_F(MountHttpNodeTest, ReadPartial) { ExpectHeaders("Range: bytes=10-18\n"); SetResponse(206, "Content-Length: 9\nContent-Range: bytes=10-18\n"); SetResponseBody("abcdefghi"); - node_->Read(10, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(10, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("abcdefghi", &buf[0]); } TEST_F(MountHttpNodeTest, ReadPartialNoServerSupport) { + int result_bytes = 0; + StringMap_t args; args["cache_content"] = "false"; SetMountArgs(args); @@ -432,6 +471,7 @@ TEST_F(MountHttpNodeTest, ReadPartialNoServerSupport) { ExpectHeaders("Range: bytes=10-18\n"); SetResponse(200, "Content-Length: 20\n"); SetResponseBody("0123456789abcdefghij"); - node_->Read(10, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(10, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("abcdefghi", &buf[0]); } diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc index 2de13cd468..bc6fa65ed6 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc @@ -6,6 +6,7 @@ #include <errno.h> #include <fcntl.h> +#include "nacl_io/error.h" #include "nacl_io/kernel_proxy.h" #include "nacl_io/mount_node.h" #include "nacl_io/mount_node_dir.h" @@ -14,31 +15,25 @@ #include "gtest/gtest.h" -#define NULL_NODE ((MountNode *) NULL) +#define NULL_NODE ((MountNode*) NULL) static int s_AllocNum = 0; class MockMemory : public MountNodeMem { public: - MockMemory() : MountNodeMem(NULL) { - s_AllocNum++; - } + MockMemory() : MountNodeMem(NULL) { s_AllocNum++; } - ~MockMemory() { - s_AllocNum--; - } + ~MockMemory() { s_AllocNum--; } - bool Init(int mode) { - return MountNodeMem::Init(mode); - } - int AddChild(const std::string& name, MountNode *node) { + Error Init(int mode) { return MountNodeMem::Init(mode); } + Error AddChild(const std::string& name, MountNode* node) { return MountNodeMem::AddChild(name, node); } - int RemoveChild(const std::string& name) { + Error RemoveChild(const std::string& name) { return MountNodeMem::RemoveChild(name); } - MountNode* FindChild(const std::string& name) { - return MountNodeMem::FindChild(name); + Error FindChild(const std::string& name, MountNode** out_node) { + return MountNodeMem::FindChild(name, out_node); } void Link() { MountNodeMem::Link(); } void Unlink() { MountNodeMem::Unlink(); } @@ -49,25 +44,19 @@ class MockMemory : public MountNodeMem { class MockDir : public MountNodeDir { public: - MockDir() : MountNodeDir(NULL) { - s_AllocNum++; - } + MockDir() : MountNodeDir(NULL) { s_AllocNum++; } - ~MockDir() { - s_AllocNum--; - } + ~MockDir() { s_AllocNum--; } - bool Init(int mode) { - return MountNodeDir::Init(mode); - } - int AddChild(const std::string& name, MountNode *node) { + Error Init(int mode) { return MountNodeDir::Init(mode); } + Error AddChild(const std::string& name, MountNode* node) { return MountNodeDir::AddChild(name, node); } - int RemoveChild(const std::string& name) { + Error RemoveChild(const std::string& name) { return MountNodeDir::RemoveChild(name); } - MountNode* FindChild(const std::string& name) { - return MountNodeDir::FindChild(name); + Error FindChild(const std::string& name, MountNode** out_node) { + return MountNodeDir::FindChild(name, out_node); } void Link() { MountNodeDir::Link(); } void Unlink() { MountNodeDir::Unlink(); } @@ -77,9 +66,12 @@ class MockDir : public MountNodeDir { }; TEST(MountNodeTest, File) { - MockMemory *file = new MockMemory; + MockMemory* file = new MockMemory; + MountNode* result_node = NULL; + size_t result_size = 0; + int result_bytes = 0; - EXPECT_TRUE(file->Init(S_IREAD | S_IWRITE)); + EXPECT_EQ(0, file->Init(S_IREAD | S_IWRITE)); // Test properties EXPECT_EQ(0, file->GetLinks()); @@ -97,12 +89,18 @@ TEST(MountNodeTest, File) { buf1[a] = a; memset(buf2, 0, sizeof(buf2)); - EXPECT_EQ(0, file->GetSize()); - EXPECT_EQ(0, file->Read(0, buf2, sizeof(buf2))); - EXPECT_EQ(0, file->GetSize()); - EXPECT_EQ(sizeof(buf1), file->Write(0, buf1, sizeof(buf1))); - EXPECT_EQ(sizeof(buf1), file->GetSize()); - EXPECT_EQ(sizeof(buf1), file->Read(0, buf2, sizeof(buf2))); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(0, result_size); + EXPECT_EQ(0, file->Read(0, buf2, sizeof(buf2), &result_bytes)); + EXPECT_EQ(0, result_bytes); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(0, result_size); + EXPECT_EQ(0, file->Write(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(sizeof(buf1), result_bytes); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(sizeof(buf1), result_size); + EXPECT_EQ(0, file->Read(0, buf2, sizeof(buf2), &result_bytes)); + EXPECT_EQ(sizeof(buf1), result_bytes); EXPECT_EQ(0, memcmp(buf1, buf2, sizeof(buf1))); struct stat s; @@ -112,20 +110,21 @@ TEST(MountNodeTest, File) { // Directory operations should fail struct dirent d; - EXPECT_EQ(-1, file->GetDents(0, &d, sizeof(d))); - EXPECT_EQ(errno, ENOTDIR); - EXPECT_EQ(-1, file->AddChild("", file)); - EXPECT_EQ(errno, ENOTDIR); - EXPECT_EQ(-1, file->RemoveChild("")); - EXPECT_EQ(errno, ENOTDIR); - EXPECT_EQ(NULL_NODE, file->FindChild("")); - EXPECT_EQ(errno, ENOTDIR); + EXPECT_EQ(ENOTDIR, file->GetDents(0, &d, sizeof(d), &result_bytes)); + EXPECT_EQ(ENOTDIR, file->AddChild("", file)); + EXPECT_EQ(ENOTDIR, file->RemoveChild("")); + EXPECT_EQ(ENOTDIR, file->FindChild("", &result_node)); + EXPECT_EQ(NULL_NODE, result_node); delete file; } TEST(MountNodeTest, Directory) { - MockDir *root = new MockDir(); + MockDir* root = new MockDir(); + MountNode* result_node = NULL; + size_t result_size = 0; + int result_bytes = 0; + root->Init(S_IREAD | S_IWRITE); // Test properties @@ -139,15 +138,14 @@ TEST(MountNodeTest, Directory) { // IO operations should fail char buf1[1024]; - EXPECT_EQ(0, root->GetSize()); - EXPECT_EQ(-1, root->Read(0, buf1, sizeof(buf1))); - EXPECT_EQ(errno, EISDIR); - EXPECT_EQ(-1, root->Write(0, buf1, sizeof(buf1))); - EXPECT_EQ(errno, EISDIR); + EXPECT_EQ(0, root->GetSize(&result_size)); + EXPECT_EQ(0, result_size); + EXPECT_EQ(EISDIR, root->Read(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(EISDIR, root->Write(0, buf1, sizeof(buf1), &result_bytes)); // Test directory operations MockMemory* file = new MockMemory; - EXPECT_TRUE(file->Init(S_IREAD | S_IWRITE)); + EXPECT_EQ(0, file->Init(S_IREAD | S_IWRITE)); EXPECT_EQ(1, root->RefCount()); EXPECT_EQ(1, file->RefCount()); @@ -157,25 +155,28 @@ TEST(MountNodeTest, Directory) { // Test that the directory is there struct dirent d; - EXPECT_EQ(sizeof(d), root->GetDents(0, &d, sizeof(d))); + EXPECT_EQ(0, root->GetDents(0, &d, sizeof(d), &result_bytes)); + EXPECT_EQ(sizeof(d), result_bytes); EXPECT_LT(0, d.d_ino); // 0 is an invalid inode number. EXPECT_EQ(sizeof(d), d.d_off); EXPECT_EQ(sizeof(d), d.d_reclen); EXPECT_EQ(0, strcmp("F1", d.d_name)); - EXPECT_EQ(0, root->GetDents(sizeof(d), &d, sizeof(d))); + EXPECT_EQ(0, root->GetDents(sizeof(d), &d, sizeof(d), &result_bytes)); + EXPECT_EQ(0, result_bytes); EXPECT_EQ(0, root->AddChild("F2", file)); EXPECT_EQ(2, file->GetLinks()); EXPECT_EQ(3, file->RefCount()); - EXPECT_EQ(-1, root->AddChild("F1", file)); - EXPECT_EQ(EEXIST, errno); + EXPECT_EQ(EEXIST, root->AddChild("F1", file)); EXPECT_EQ(2, file->GetLinks()); EXPECT_EQ(2, s_AllocNum); - EXPECT_NE(NULL_NODE, root->FindChild("F1")); - EXPECT_NE(NULL_NODE, root->FindChild("F2")); - EXPECT_EQ(NULL_NODE, root->FindChild("F3")); - EXPECT_EQ(errno, ENOENT); + EXPECT_EQ(0, root->FindChild("F1", &result_node)); + EXPECT_NE(NULL_NODE, result_node); + EXPECT_EQ(0, root->FindChild("F2", &result_node)); + EXPECT_NE(NULL_NODE, result_node); + EXPECT_EQ(ENOENT, root->FindChild("F3", &result_node)); + EXPECT_EQ(NULL_NODE, result_node); EXPECT_EQ(2, s_AllocNum); EXPECT_EQ(0, root->RemoveChild("F1")); diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc index 1604ccafd3..245d13daf4 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc @@ -22,12 +22,10 @@ class MountMemMock : public MountMem { public: MountMemMock() { StringMap_t map; - Init(1, map, NULL); - }; - - int num_nodes() { - return (int) inode_pool_.size(); + EXPECT_EQ(0, Init(1, map, NULL)); } + + int num_nodes() { return (int) inode_pool_.size(); } }; class MountDevMock : public MountDev { @@ -36,20 +34,20 @@ class MountDevMock : public MountDev { StringMap_t map; Init(1, map, NULL); } - int num_nodes() { - return (int) inode_pool_.size(); - } + int num_nodes() { return (int) inode_pool_.size(); } }; } // namespace - -#define NULL_NODE ((MountNode *) NULL) +#define NULL_NODE ((MountNode*) NULL) TEST(MountTest, Sanity) { MountMemMock* mnt = new MountMemMock(); MountNode* file; MountNode* root; + MountNode* result_node; + size_t result_size = 0; + int result_bytes = 0; char buf1[1024]; @@ -57,43 +55,54 @@ TEST(MountTest, Sanity) { EXPECT_EQ(1, mnt->num_nodes()); // Fail to open non existent file - EXPECT_EQ(NULL_NODE, mnt->Open(Path("/foo"), O_RDWR)); - EXPECT_EQ(errno, ENOENT); + EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &result_node)); + EXPECT_EQ(NULL, result_node); // Create a file - file = mnt->Open(Path("/foo"), O_RDWR | O_CREAT); + EXPECT_EQ(0, mnt->Open(Path("/foo"), O_RDWR | O_CREAT, &file)); EXPECT_NE(NULL_NODE, file); - if (file == NULL) return; + if (file == NULL) + return; EXPECT_EQ(2, file->RefCount()); EXPECT_EQ(2, mnt->num_nodes()); + // Open the root directory for write should fail. + EXPECT_EQ(EISDIR, mnt->Open(Path("/"), O_RDWR, &root)); + // Open the root directory - root = mnt->Open(Path("/"), O_RDWR); + EXPECT_EQ(0, mnt->Open(Path("/"), O_RDONLY, &root)); EXPECT_NE(NULL_NODE, root); if (NULL != root) { struct dirent dirs[2]; - int len = root->GetDents(0, dirs, sizeof(dirs)); + int len; + EXPECT_EQ(0, root->GetDents(0, dirs, sizeof(dirs), &len)); EXPECT_EQ(sizeof(struct dirent), len); } // Fail to re-create the same file - EXPECT_EQ(NULL_NODE, mnt->Open(Path("/foo"), O_RDWR | O_CREAT | O_EXCL)); - EXPECT_EQ(errno, EEXIST); + EXPECT_EQ(EEXIST, + mnt->Open(Path("/foo"), O_RDWR | O_CREAT | O_EXCL, &result_node)); + EXPECT_EQ(NULL_NODE, result_node); EXPECT_EQ(2, mnt->num_nodes()); // Fail to create a directory with the same name - EXPECT_EQ(-1, mnt->Mkdir(Path("/foo"), O_RDWR)); - EXPECT_EQ(errno, EEXIST); + EXPECT_EQ(EEXIST, mnt->Mkdir(Path("/foo"), O_RDWR)); // Attempt to READ/WRITE - EXPECT_EQ(0, file->GetSize()); - EXPECT_EQ(sizeof(buf1), file->Write(0, buf1, sizeof(buf1))); - EXPECT_EQ(sizeof(buf1), file->GetSize()); - EXPECT_EQ(sizeof(buf1), file->Read(0, buf1, sizeof(buf1))); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(0, result_size); + EXPECT_EQ(0, file->Write(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(sizeof(buf1), result_bytes); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(sizeof(buf1), result_size); + EXPECT_EQ(0, file->Read(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(sizeof(buf1), result_bytes); // Attempt to open the same file - EXPECT_EQ(file, mnt->Open(Path("/foo"), O_RDWR | O_CREAT)); - EXPECT_EQ(sizeof(buf1), file->GetSize()); + EXPECT_EQ(0, mnt->Open(Path("/foo"), O_RDWR | O_CREAT, &result_node)); + EXPECT_EQ(file, result_node); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(sizeof(buf1), result_size); EXPECT_EQ(3, file->RefCount()); EXPECT_EQ(2, mnt->num_nodes()); @@ -102,9 +111,8 @@ TEST(MountTest, Sanity) { EXPECT_EQ(2, mnt->num_nodes()); EXPECT_EQ(0, mnt->Unlink(Path("/foo"))); EXPECT_EQ(2, mnt->num_nodes()); - EXPECT_EQ(-1, mnt->Unlink(Path("/foo"))); + EXPECT_EQ(ENOENT, mnt->Unlink(Path("/foo"))); EXPECT_EQ(2, mnt->num_nodes()); - EXPECT_EQ(errno, ENOENT); mnt->ReleaseNode(file); EXPECT_EQ(1, mnt->num_nodes()); @@ -112,13 +120,13 @@ TEST(MountTest, Sanity) { EXPECT_EQ(0, mnt->Mkdir(Path("/foo"), O_RDWR)); // Create a file (exclusively) - file = mnt->Open(Path("/foo/bar"), O_RDWR | O_CREAT | O_EXCL); + EXPECT_EQ(0, mnt->Open(Path("/foo/bar"), O_RDWR | O_CREAT | O_EXCL, &file)); EXPECT_NE(NULL_NODE, file); - if (NULL == file) return; + if (NULL == file) + return; // Attempt to delete the directory - EXPECT_EQ(-1, mnt->Rmdir(Path("/foo"))); - EXPECT_EQ(errno, ENOTEMPTY); + EXPECT_EQ(ENOTEMPTY, mnt->Rmdir(Path("/foo"))); // Unlink the file, then delete the directory EXPECT_EQ(0, mnt->Unlink(Path("/foo/bar"))); @@ -128,60 +136,71 @@ TEST(MountTest, Sanity) { EXPECT_EQ(1, mnt->num_nodes()); // Verify the directory is gone - file = mnt->Open(Path("/foo"), O_RDWR); + EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &file)); EXPECT_EQ(NULL_NODE, file); - EXPECT_EQ(errno, ENOENT); } TEST(MountTest, MemMountRemove) { MountMemMock* mnt = new MountMemMock(); MountNode* file; + MountNode* result_node; EXPECT_EQ(0, mnt->Mkdir(Path("/dir"), O_RDWR)); - file = mnt->Open(Path("/file"), O_RDWR | O_CREAT | O_EXCL); + EXPECT_EQ(0, mnt->Open(Path("/file"), O_RDWR | O_CREAT | O_EXCL, &file)); EXPECT_NE(NULL_NODE, file); mnt->ReleaseNode(file); EXPECT_EQ(0, mnt->Remove(Path("/dir"))); EXPECT_EQ(0, mnt->Remove(Path("/file"))); - EXPECT_EQ(NULL_NODE, mnt->Open(Path("/dir/foo"), O_CREAT | O_RDWR)); - EXPECT_EQ(ENOENT, errno); - EXPECT_EQ(NULL_NODE, mnt->Open(Path("/file"), O_RDONLY)); - EXPECT_EQ(ENOENT, errno); + EXPECT_EQ(ENOENT, + mnt->Open(Path("/dir/foo"), O_CREAT | O_RDWR, &result_node)); + EXPECT_EQ(NULL_NODE, result_node); + EXPECT_EQ(ENOENT, mnt->Open(Path("/file"), O_RDONLY, &result_node)); + EXPECT_EQ(NULL_NODE, result_node); } TEST(MountTest, DevNull) { MountDevMock* mnt = new MountDevMock(); - MountNode* dev_null = mnt->Open(Path("/null"), O_RDWR); + MountNode* dev_null = NULL; + int result_bytes = 0; + + ASSERT_EQ(0, mnt->Open(Path("/null"), O_RDWR, &dev_null)); ASSERT_NE(NULL_NODE, dev_null); // Writing to /dev/null should write everything. const char msg[] = "Dummy test message."; - EXPECT_EQ(strlen(msg), dev_null->Write(0, &msg[0], strlen(msg))); + EXPECT_EQ(0, dev_null->Write(0, &msg[0], strlen(msg), &result_bytes)); + EXPECT_EQ(strlen(msg), result_bytes); // Reading from /dev/null should read nothing. const int kBufferLength = 100; char buffer[kBufferLength]; - EXPECT_EQ(0, dev_null->Read(0, &buffer[0], kBufferLength)); + EXPECT_EQ(0, dev_null->Read(0, &buffer[0], kBufferLength, &result_bytes)); + EXPECT_EQ(0, result_bytes); mnt->ReleaseNode(dev_null); } TEST(MountTest, DevZero) { MountDevMock* mnt = new MountDevMock(); - MountNode* dev_zero = mnt->Open(Path("/zero"), O_RDWR); + MountNode* dev_zero = NULL; + int result_bytes = 0; + + ASSERT_EQ(0, mnt->Open(Path("/zero"), O_RDWR, &dev_zero)); ASSERT_NE(NULL_NODE, dev_zero); // Writing to /dev/zero should write everything. const char msg[] = "Dummy test message."; - EXPECT_EQ(strlen(msg), dev_zero->Write(0, &msg[0], strlen(msg))); + EXPECT_EQ(0, dev_zero->Write(0, &msg[0], strlen(msg), &result_bytes)); + EXPECT_EQ(strlen(msg), result_bytes); // Reading from /dev/zero should read all zeroes. const int kBufferLength = 100; char buffer[kBufferLength]; // First fill with all 1s. memset(&buffer[0], 0x1, kBufferLength); - EXPECT_EQ(kBufferLength, dev_zero->Read(0, &buffer[0], kBufferLength)); + EXPECT_EQ(0, dev_zero->Read(0, &buffer[0], kBufferLength, &result_bytes)); + EXPECT_EQ(kBufferLength, result_bytes); char zero_buffer[kBufferLength]; memset(&zero_buffer[0], 0, kBufferLength); @@ -191,12 +210,16 @@ TEST(MountTest, DevZero) { TEST(MountTest, DevUrandom) { MountDevMock* mnt = new MountDevMock(); - MountNode* dev_urandom = mnt->Open(Path("/urandom"), O_RDWR); + MountNode* dev_urandom = NULL; + int result_bytes = 0; + + ASSERT_EQ(0, mnt->Open(Path("/urandom"), O_RDWR, &dev_urandom)); ASSERT_NE(NULL_NODE, dev_urandom); - // Writing to /dev/zero should write everything. + // Writing to /dev/urandom should write everything. const char msg[] = "Dummy test message."; - EXPECT_EQ(strlen(msg), dev_urandom->Write(0, &msg[0], strlen(msg))); + EXPECT_EQ(0, dev_urandom->Write(0, &msg[0], strlen(msg), &result_bytes)); + EXPECT_EQ(strlen(msg), result_bytes); // Reading from /dev/urandom should read random bytes. const int kSampleBatches = 1000; @@ -207,7 +230,9 @@ TEST(MountTest, DevUrandom) { unsigned char buffer[kSampleBatchSize]; for (int batch = 0; batch < kSampleBatches; ++batch) { - int bytes_read = dev_urandom->Read(0, &buffer[0], kSampleBatchSize); + int bytes_read = 0; + EXPECT_EQ(0, + dev_urandom->Read(0, &buffer[0], kSampleBatchSize, &bytes_read)); EXPECT_EQ(kSampleBatchSize, bytes_read); for (int i = 0; i < bytes_read; ++i) { diff --git a/native_client_sdk/src/libraries/ppapi_cpp/library.dsc b/native_client_sdk/src/libraries/ppapi_cpp/library.dsc index 2914c92a59..e6ffb6c26c 100644 --- a/native_client_sdk/src/libraries/ppapi_cpp/library.dsc +++ b/native_client_sdk/src/libraries/ppapi_cpp/library.dsc @@ -18,6 +18,7 @@ 'audio.cc', 'audio_config.cc', 'core.cc', + 'directory_entry.cc', 'file_io.cc', 'file_ref.cc', 'file_system.cc', diff --git a/native_client_sdk/src/libraries/ppapi_cpp_private/library.dsc b/native_client_sdk/src/libraries/ppapi_cpp_private/library.dsc index 50b9201a4d..8646c3a110 100644 --- a/native_client_sdk/src/libraries/ppapi_cpp_private/library.dsc +++ b/native_client_sdk/src/libraries/ppapi_cpp_private/library.dsc @@ -12,6 +12,7 @@ 'file_io_private.cc', 'host_resolver_private.cc', 'net_address_private.cc', + 'pass_file_handle.cc', 'tcp_socket_private.cc', 'tcp_server_socket_private.cc', 'udp_socket_private.cc', diff --git a/native_client_sdk/src/libraries/ppapi_main/library.dsc b/native_client_sdk/src/libraries/ppapi_main/library.dsc deleted file mode 100644 index c65e93036f..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/library.dsc +++ /dev/null @@ -1,31 +0,0 @@ -{ - 'TOOLS': ['newlib', 'glibc', 'pnacl'], - 'TARGETS': [ - { - 'NAME': 'ppapi_main', - 'TYPE': 'static-lib', - 'SOURCES' : [ - "ppapi_instance.cc", - "ppapi_instance2d.cc", - "ppapi_instance3d.cc", - "ppapi_main.cc", - "ppapi_queue.cc", - ], - } - ], - 'HEADERS': [ - { - 'FILES': [ - "ppapi_event.h", - "ppapi_instance.h", - "ppapi_instance2d.h", - "ppapi_instance3d.h", - "ppapi_main.h", - "ppapi_queue.h", - ], - 'DEST': 'include/ppapi_main', - }, - ], - 'DEST': 'src', - 'NAME': 'ppapi_main', -} diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_event.h b/native_client_sdk/src/libraries/ppapi_main/ppapi_event.h deleted file mode 100644 index d991192fe6..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_event.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -#ifndef PPAPI_MAIN_PPAPI_EVENT_H_ -#define PPAPI_MAIN_PPAPI_EVENT_H_ - -#include "ppapi/c/pp_input_event.h" -#include "ppapi/c/pp_point.h" -#include "ppapi/c/pp_touch_point.h" -#include "ppapi/c/ppb_input_event.h" - -#include "utils/macros.h" - -EXTERN_C_BEGIN - -/* - * Event Structures - * - * The following event structures are based on the equivalent structs - * defined in ppapi/c/ppb_input_event.h. - * - */ - -// Generic Event -typedef struct { - PP_InputEvent_Type event_type; - PP_TimeTicks time_ticks; - uint32_t modifiers; -} PPAPIEvent; - - -// Key Code Up/Down -typedef struct { - PPAPIEvent event; - - uint32_t key_code; -} PPAPIKeyEvent; - - -// Cooked Character Event -typedef struct { - PPAPIEvent event; - - char text[5]; -} PPAPICharEvent; - - -// Mouse Event -typedef struct { - PPAPIEvent event; - - PP_InputEvent_MouseButton button; - struct PP_Point location; - struct PP_Point delta; -} PPAPIMouseEvent; - - -// Wheel Event -typedef struct { - PPAPIEvent event; - - PP_InputEvent_MouseButton button; - struct PP_FloatPoint delta; - uint32_t by_page; -} PPAPIWheelEvent; - - -// Touch Event -#define MAX_TOUCH_POINTS 4 -typedef struct { - PPAPIEvent event; - - uint32_t point_count; - struct PP_TouchPoint points[MAX_TOUCH_POINTS]; -} PPAPITouchEvent; - -EXTERN_C_END - -#endif // PPAPI_MAIN_PPAPI_EVENT_H_ diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance.cc b/native_client_sdk/src/libraries/ppapi_main/ppapi_instance.cc deleted file mode 100644 index 2816c74817..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance.cc +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <fcntl.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include <cstdlib> -#include <cstring> -#include <map> -#include <string> -#include <vector> - -#include "nacl_io/kernel_wrap.h" -#include "nacl_io/nacl_io.h" - -#include "ppapi/cpp/input_event.h" -#include "ppapi/cpp/message_loop.h" -#include "ppapi/cpp/rect.h" -#include "ppapi/cpp/size.h" -#include "ppapi/cpp/touch_point.h" -#include "ppapi/cpp/var.h" - -#include "ppapi_main/ppapi_event.h" -#include "ppapi_main/ppapi_instance.h" -#include "ppapi_main/ppapi_main.h" - - -PPAPIInstance* PPAPI_GetInstance() { - return static_cast<PPAPIInstance*>(PPAPI_GetInstanceObject()); -} - -struct StartInfo { - PPAPIInstance* inst_; - uint32_t argc_; - const char** argv_; -}; - - -// -// The starting point for 'main'. We create this thread to hide the real -// main pepper thread which must never be blocked. -// -void* PPAPIInstance::MainThreadThunk(void *info) { - StartInfo* si = static_cast<StartInfo*>(info); - int ret = si->inst_->MainThread(si->argc_, si->argv_); - - printf("Main thread returned with %d.\n", ret); - for (uint32_t i = 0; i < si->argc_; i++) { - delete[] si->argv_[i]; - } - delete[] si->argv_; - delete si; - - return NULL; -} - -// -// Enabled with pm_use_main=False this creates a second thread where we -// send all input, view change, buffer swap, etc... messages. This allows us -// avoid blocking the main pepper thread which would otherwise recieves these -// messages, and may need to lock to act on them. -// -void *PPAPIInstance::EventThreadThunk(void *this_ptr) { - PPAPIInstance *pInst = static_cast<PPAPIInstance*>(this_ptr); - return pInst->EventThread(); -} - - -// -// The default implementation supports running a 'C' main. -// -int PPAPIInstance::MainThread(int argc, const char *argv[]) { - return ppapi_main(argc, argv); -} - - -// -// The default implementation just waits to processes forwarded events. -// -void* PPAPIInstance::EventThread() { - render_loop_.AttachToCurrentThread(); - render_loop_.Run(); - printf("Event thread exiting.\n"); - return NULL; -} - - -PPAPIInstance::PPAPIInstance(PP_Instance instance, const char *args[]) - : pp::Instance(instance), - main_loop_(this), - has_focus_(false), - fullscreen_(this), - is_context_bound_(false), - callback_factory_(this), - use_main_thread_(true), - render_loop_(this) { - - // Place PPAPI_MAIN_USE arguments into properties map - while (*args) { - std::string key = *args++; - std::string val = *args++; - properties_[key] = val; - } - - RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | - PP_INPUTEVENT_CLASS_KEYBOARD | - PP_INPUTEVENT_CLASS_WHEEL | - PP_INPUTEVENT_CLASS_TOUCH); -} - -PPAPIInstance::~PPAPIInstance() {} - - -bool PPAPIInstance::Init(uint32_t arg, - const char* argn[], - const char* argv[]) { - StartInfo* si = new StartInfo; - - - si->inst_ = this; - si->argc_ = 1; - si->argv_ = new const char *[arg*2+1]; - si->argv_[0] = NULL; - - // Process arguments passed into Module INIT from JavaScript - for (uint32_t i=0; i < arg; i++) { - if (argv[i]) { - printf("ARG %s=%s\n", argn[i], argv[i]); - } else { - printf("ARG %s\n", argn[i]); - } - - // If we start with PM prefix set the instance argument map - if (0 == strncmp(argn[i], "pm_", 3)) { - std::string key = argn[i]; - std::string val = argv[i]; - properties_[key] = val; - continue; - } - - // If this is the 'src' tag, then get the NMF name. - if (!strcmp("src", argn[i])) { - char *name = new char[strlen(argv[i]) + 1]; - strcpy(name, argv[i]); - si->argv_[0] = name; - } - else { - // Otherwise turn it into arguments - char *key = new char[strlen(argn[i]) + 3]; - key[0] = '-'; - key[1] = '-'; - strcpy(&key[2], argn[i]); - - si->argv_[si->argc_++] = key; - if (argv[i] && argv[i][0]) { - char *val = new char[strlen(argv[i]) + 1]; - strcpy(val, argv[i]); - si->argv_[si->argc_++] = val; - } - } - } - - // If src was not found, set the first arg to something - if (NULL == si->argv_[0]) { - char *name = new char[5]; - strcpy(name, "NMF?"); - si->argv_[0] = name; - } - - - if (ProcessProperties()) { - pthread_t main_thread; - int ret = pthread_create(&main_thread, NULL, MainThreadThunk, - static_cast<void*>(si)); - return ret == 0; - } - - return false; -} - -const char* PPAPIInstance::GetProperty(const char* key, const char* def) { - PropertyMap_t::iterator it = properties_.find(key); - if (it != properties_.end()) { - return it->second.c_str(); - } - return def; -} - -bool PPAPIInstance::ProcessProperties() { - const char* stdin_path = GetProperty("pm_stdin", "/dev/stdin"); - const char* stdout_path = GetProperty("pm_stdout", "/dev/stdout"); - const char* stderr_path = GetProperty("pm_stderr", "/dev/console3"); - const char* queue_size = GetProperty("pm_queue_size", "1024"); - - // Build the event Queue with a minimum size of 4 - uint32_t queue_size_int = atoi(queue_size); - if (queue_size_int < 4) queue_size_int = 4; - event_queue_.SetSize(queue_size_int); - - // Enable NaCl IO to map STDIN, STDOUT, and STDERR - nacl_io_init_ppapi(PPAPI_GetInstanceId(), PPAPI_GetInterface); - int fd0 = open(stdin_path, O_RDONLY); - dup2(fd0, 0); - - int fd1 = open(stdout_path, O_WRONLY); - dup2(fd1, 1); - - int fd2 = open(stderr_path, O_WRONLY); - dup2(fd2, 2); - - setvbuf(stderr, NULL, _IOLBF, 0); - setvbuf(stdout, NULL, _IOLBF, 0); - - const char *use_main_str = GetProperty("pm_use_main", "true"); - use_main_thread_ = !strcasecmp(use_main_str, "true"); - - // Create seperate thread for processing events. - printf("Events on main thread = %s.\n", use_main_str); - if (!use_main_thread_) { - pthread_t event_thread; - int ret = pthread_create(&event_thread, NULL, EventThreadThunk, - static_cast<void*>(this)); - return ret == 0; - } - return true; -} - -void PPAPIInstance::HandleMessage(const pp::Var& message) { -} - -bool PPAPIInstance::HandleInputEvent(const pp::InputEvent& event) { - PPAPIEvent* event_ptr; - - // Remove a stale message if one is available - event_ptr = static_cast<PPAPIEvent*>(event_queue_.RemoveStaleMessage()); - delete event_ptr; - - switch (event.GetType()) { - case PP_INPUTEVENT_TYPE_MOUSEDOWN: - case PP_INPUTEVENT_TYPE_MOUSEUP: - case PP_INPUTEVENT_TYPE_MOUSEMOVE: - case PP_INPUTEVENT_TYPE_MOUSEENTER: - case PP_INPUTEVENT_TYPE_MOUSELEAVE: { - pp::MouseInputEvent mouse_event(event); - PPAPIMouseEvent* mouse_ptr = new PPAPIMouseEvent; - mouse_ptr->button = mouse_event.GetButton(); - mouse_ptr->location = mouse_event.GetPosition().pp_point(); - mouse_ptr->delta = mouse_event.GetMovement().pp_point(); - event_ptr = &mouse_ptr->event; - break; - } - - case PP_INPUTEVENT_TYPE_WHEEL: { - pp::WheelInputEvent wheel_event(event); - PPAPIWheelEvent* wheel_ptr = new PPAPIWheelEvent; - wheel_ptr->by_page = - static_cast<uint32_t>(wheel_event.GetScrollByPage()); - wheel_ptr->delta = wheel_event.GetDelta().pp_float_point(); - event_ptr = &wheel_ptr->event; - break; - } - - case PP_INPUTEVENT_TYPE_CHAR: { - pp::KeyboardInputEvent key_event(event); - PPAPICharEvent* char_ptr = new PPAPICharEvent; - strncpy(char_ptr->text, - key_event.GetCharacterText().DebugString().c_str(), - sizeof(char_ptr->text)); - event_ptr = &char_ptr->event; - break; - } - - case PP_INPUTEVENT_TYPE_RAWKEYDOWN: - case PP_INPUTEVENT_TYPE_KEYDOWN: - case PP_INPUTEVENT_TYPE_KEYUP: { - pp::KeyboardInputEvent key_event(event); - PPAPIKeyEvent* key_ptr = new PPAPIKeyEvent; - key_ptr->key_code = key_event.GetKeyCode(); - event_ptr = &key_ptr->event; - break; - } - - case PP_INPUTEVENT_TYPE_TOUCHSTART: - case PP_INPUTEVENT_TYPE_TOUCHMOVE: - case PP_INPUTEVENT_TYPE_TOUCHEND: - case PP_INPUTEVENT_TYPE_TOUCHCANCEL: { - pp::TouchInputEvent touch_event(event); - PPAPITouchEvent* touch_ptr = new PPAPITouchEvent; - touch_ptr->point_count = - touch_event.GetTouchCount(PP_TOUCHLIST_TYPE_TOUCHES); - for (uint32_t cnt = 0; cnt < touch_ptr->point_count; cnt++) { - pp::TouchPoint *pnt = (pp::TouchPoint*) &touch_ptr->points[cnt]; - *pnt = touch_event.GetTouchByIndex(PP_TOUCHLIST_TYPE_TOUCHES ,cnt); - } - event_ptr = &touch_ptr->event; - break; - } - - default: - fprintf(stderr, "Unhandled event type %d\n", event.GetType()); - return false; - } - - event_ptr->event_type = event.GetType(); - event_ptr->time_ticks = event.GetTimeStamp(); - event_ptr->modifiers = event.GetModifiers(); - if (!event_queue_.AddNewMessage(event_ptr)) { - printf("Warning: Event Queue is full, dropping message.\n"); - } - return true; -} - -PPAPIEvent* PPAPIInstance::AcquireInputEvent() { - return static_cast<PPAPIEvent*>(event_queue_.AcquireTopMessage()); -} - -void PPAPIInstance::ReleaseInputEvent(PPAPIEvent* event) { - event_queue_.ReleaseTopMessage(event); -} - -void PPAPIInstance::DidChangeView(const pp::View& view) { - pp::Size new_size = view.GetRect().size(); - printf("View changed: %dx%d\n", new_size.width(), new_size.height()); - - // Build or update the 3D context when the view changes. - if (use_main_thread_) { - // If using the main thread, update the context immediately - BuildContext(0, new_size); - } else { - // If using a seperate thread, then post the message so we can build the - // context on the correct thread. - render_loop_.PostWork(callback_factory_.NewCallback( - &PPAPIInstance::BuildContext, new_size)); - } -} - -void PPAPIInstance::DidChangeFocus(bool focus) { - has_focus_ = focus; -} - -bool PPAPIInstance::ToggleFullscreen() { - // Ignore switch if in transition - if (!is_context_bound_) - return false; - - if (fullscreen_.IsFullscreen()) { - if (!fullscreen_.SetFullscreen(false)) { - printf("Could not leave fullscreen mode\n"); - return false; - } - } else { - if (!fullscreen_.SetFullscreen(true)) { - printf("Could not enter fullscreen mode\n"); - return false; - } - } - return true; -} - -// The default implementation calls the 'C' render function. -void PPAPIInstance::Render(PP_Resource ctx, uint32_t width, uint32_t height) { -} - -void PPAPIInstance::Flushed(int32_t result) { -} - -void PPAPIInstance::BuildContext(int32_t result, const pp::Size& new_size) { - size_ = new_size; - printf("Resized: %dx%d.\n", size_.width(), size_.height()); -} diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance.h b/native_client_sdk/src/libraries/ppapi_main/ppapi_instance.h deleted file mode 100644 index 1172cd8c45..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance.h +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PPAPI_MAIN_PPAPI_INSTANCE_H_ -#define PPAPI_MAIN_PPAPI_INSTANCE_H_ - -#include <map> - -#include "ppapi/c/pp_instance.h" -#include "ppapi/cpp/fullscreen.h" -#include "ppapi/cpp/instance.h" -#include "ppapi/cpp/message_loop.h" -#include "ppapi/cpp/mouse_lock.h" - -#include "ppapi/utility/completion_callback_factory.h" - -#include "ppapi_main/ppapi_event.h" -#include "ppapi_main/ppapi_queue.h" - - -typedef std::map<std::string, std::string> PropertyMap_t; - -class PPAPIInstance : public pp::Instance { - public: - PPAPIInstance(PP_Instance instance, const char *args[]); - virtual ~PPAPIInstance(); - - // - // Callback functions triggered by Pepepr - // - - // Called by the browser when the NaCl module is loaded and all ready to go. - virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); - - // Called whenever the in-browser window changes size. - virtual void DidChangeView(const pp::View& view); - - // Called by the browser when the NaCl canvas gets or loses focus. - virtual void DidChangeFocus(bool has_focus); - - // Called by the browser to handle the postMessage() call in Javascript. - virtual void HandleMessage(const pp::Var& message); - - // Called by the browser to handle incoming input events. - virtual bool HandleInputEvent(const pp::InputEvent& event); - - // Called when the graphics flush completes - virtual void Flushed(int result); - - // Called when we need to rebuild the Graphics device context. This usually - // happens as a result of DidChangeView. - virtual void BuildContext(int32_t result, const pp::Size& new_size); - - // Called with the current graphics context, current size and width, when - // the application is ready to render, either due to a newly build - // Context, or a successful Flush of the previous frame. - virtual void Render(PP_Resource ctx, uint32_t width, uint32_t height); - - // - // Thread entry points - // - virtual int MainThread(int argc, const char* argv[]); - virtual void* EventThread(); - - // - // Request API - // - bool ToggleFullscreen(); - virtual PPAPIEvent* AcquireInputEvent(); - virtual void ReleaseInputEvent(PPAPIEvent* event); - static PPAPIInstance* GetInstance(); - - protected: - // Called to run ppapi_main. - static void* MainThreadThunk(void *start_info); - - // Called if message processing and rendering happens off the main thread. - static void* EventThreadThunk(void *this_ptr); - - // Called by Init to processes default and embed tag arguments prior to - // launching the 'ppapi_main' thread. - virtual bool ProcessProperties(); - - // Returns value based on KEY or default. - const char* GetProperty(const char* key, const char* def = NULL); - - protected: - pp::MessageLoop main_loop_; - pp::Fullscreen fullscreen_; - pp::MessageLoop render_loop_; - pp::CompletionCallbackFactory<PPAPIInstance> callback_factory_; - - PropertyMap_t properties_; - PPAPIQueue event_queue_; - - pp::Size size_; - bool has_focus_; - bool is_context_bound_; - bool was_fullscreen_; - bool mouse_locked_; - bool use_main_thread_; -}; - - -#endif // PPAPI_MAIN_PPAPI_INSTANCE_H_ diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance2d.cc b/native_client_sdk/src/libraries/ppapi_main/ppapi_instance2d.cc deleted file mode 100644 index f6e54be62e..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance2d.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stdio.h> - -#include "ppapi/cpp/graphics_2d.h" -#include "ppapi/cpp/message_loop.h" -#include "ppapi/cpp/size.h" -#include "ppapi/cpp/var.h" - -#include "ppapi/utility/completion_callback_factory.h" - -#include "ppapi_main/ppapi_instance2d.h" -#include "ppapi_main/ppapi_main.h" - - -void* PPAPI_CreateInstance2D(PP_Instance inst, const char *args[]) { - return static_cast<void*>(new PPAPIInstance2D(inst, args)); -} - - -PPAPIInstance2D* PPAPIInstance2D::GetInstance2D() { - return static_cast<PPAPIInstance2D*>(PPAPI_GetInstanceObject()); -} - - -void PPAPIInstance2D::Flushed(int result) { - if (result != 0) { - printf("Flush result=%d.\n", result); - } - - if (is_context_bound_) { - Render(device_context_.pp_resource(), size_.width(), size_.height()); - - int result; - result = device_context_.Flush(callback_factory_.NewCallback( - &PPAPIInstance::Flushed)); - if (result == PP_OK_COMPLETIONPENDING) return; - printf("Failed Flush with %d.\n", result); - } - - // Failed to draw, so add a callback for the future. This could - // an application choice or the browser dealing with an event such as - // fullscreen toggle. We add a delay of 100ms (to prevent burnning CPU - // in cases where the context will not be available for a while. - pp::MessageLoop::GetCurrent().PostWork(callback_factory_.NewCallback( - &PPAPIInstance::Flushed), 100); -} - -void PPAPIInstance2D::BuildContext(int32_t result, const pp::Size& new_size) { - printf("Building context.\n"); - - size_ = new_size; - device_context_ = pp::Graphics2D(this, size_ ,true); - printf("Got Context!\n"); - - is_context_bound_ = BindGraphics(device_context_); - printf("Context is bound=%d\n", is_context_bound_); - - if (is_context_bound_) { - PPAPIBuildContext(size_.width(), size_.height()); - device_context_.Flush(callback_factory_.NewCallback( - &PPAPIInstance::Flushed)); - } else { - fprintf(stderr, "Failed to bind context for %dx%d.\n", size_.width(), - size_.height()); - } -} - -// The default implementation calls the 'C' render function. -void PPAPIInstance2D::Render(PP_Resource ctx, uint32_t width, - uint32_t height) { - PPAPIRender(ctx, width, height); -} - -PPAPIInstance2D::PPAPIInstance2D(PP_Instance instance, const char *args[]) - : PPAPIInstance(instance, args) { -} - - -PPAPIInstance2D::~PPAPIInstance2D() { - if (is_context_bound_) { - is_context_bound_ = false; - // Cleanup code? - } -} diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance2d.h b/native_client_sdk/src/libraries/ppapi_main/ppapi_instance2d.h deleted file mode 100644 index ac600c2c27..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance2d.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PPAPI_MAIN_PPAPI_INSTANCE2D_H_ -#define PPAPI_MAIN_PPAPI_INSTANCE2D_H_ - -#include "ppapi/c/pp_instance.h" - -#include "ppapi/cpp/graphics_2d.h" -#include "ppapi/cpp/instance.h" -#include "ppapi/cpp/size.h" - -#include "ppapi_main/ppapi_instance.h" - - -class PPAPIInstance2D : public PPAPIInstance { - public: - PPAPIInstance2D(PP_Instance instance, const char *args[]); - virtual ~PPAPIInstance2D(); - - // Called when we need to rebuild the context - virtual void BuildContext(int32_t result, const pp::Size& new_size); - - // Called whenever a swap takes place - virtual void Flushed(int result); - - virtual void Render(PP_Resource ctx, uint32_t width, uint32_t height); - static PPAPIInstance2D* GetInstance2D(); - - protected: - pp::Graphics2D device_context_; -}; - - -#endif // PPAPI_MAIN_PPAPI_INSTANCE2D_H_ diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance3d.cc b/native_client_sdk/src/libraries/ppapi_main/ppapi_instance3d.cc deleted file mode 100644 index 43cad475d3..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance3d.cc +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stdio.h> - -#include "GLES2/gl2.h" - -#include "ppapi/cpp/graphics_3d.h" -#include "ppapi/cpp/size.h" -#include "ppapi/gles2/gl2ext_ppapi.h" -#include "ppapi/utility/completion_callback_factory.h" - -#include "ppapi_main/ppapi_instance.h" -#include "ppapi_main/ppapi_instance3d.h" -#include "ppapi_main/ppapi_main.h" - - -void* PPAPI_CreateInstance3D(PP_Instance inst, const char *args[]) { - return static_cast<void*>(new PPAPIInstance3D(inst, args)); -} - - -int32_t *PPAPIGet3DAttribs(uint32_t width, uint32_t height) { - static int32_t attribs[] = { - PP_GRAPHICS3DATTRIB_WIDTH, 0, - PP_GRAPHICS3DATTRIB_HEIGHT, 0, - PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, - PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, - PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8, - PP_GRAPHICS3DATTRIB_SAMPLES, 0, - PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, - PP_GRAPHICS3DATTRIB_NONE - }; - - attribs[1] = width; - attribs[3] = height; - - printf("Building attribs for %dx%d.\n", width, height); - return attribs; -} - -void PPAPIBuildContext(uint32_t width, uint32_t height) { - glViewport(0, 0, width, height); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - printf("Built Context %d, %d.\n", width, height); -} - - -PPAPIInstance3D* PPAPIInstance3D::GetInstance3D() { - return static_cast<PPAPIInstance3D*>(PPAPI_GetInstanceObject()); -} - -void PPAPIInstance3D::Flushed(int result) { - if (result != 0) { - printf("Swapped result=%d.\n", result); - } - - if (is_context_bound_) { - Render(device_context_.pp_resource(), size_.width(), size_.height()); - - int result; - result = device_context_.SwapBuffers(callback_factory_.NewCallback( - &PPAPIInstance::Flushed)); - if (result == PP_OK_COMPLETIONPENDING) return; - printf("Failed swap with %d.\n", result); - } - - // Failed to draw, so add a callback for the future. This could - // an application choice or the browser dealing with an event such as - // fullscreen toggle. We add a delay of 100ms (to prevent burnning CPU - // in cases where the context will not be available for a while. - pp::MessageLoop::GetCurrent().PostWork(callback_factory_.NewCallback( - &PPAPIInstance::Flushed), 100); -} - -void PPAPIInstance3D::BuildContext(int32_t result, const pp::Size& new_size) { - printf("Building context.\n"); - - // If already bound, try to resize to avoid the need to rebuild the context. - if (is_context_bound_) { - // If the size is correct, then just skip this request. - if (new_size == size_) { - printf("Skipped building context, same size as bound.\n"); - return; - } - int err = device_context_.ResizeBuffers(new_size.width(), - new_size.height()); - - // Resized the context, we are done - if (err == PP_OK) { - size_ = new_size; - fprintf(stderr, "Resized context from %dx%d to %dx%d", - size_.width(), size_.height(), new_size.width(), - new_size.height()); - PPAPIBuildContext(size_.width(), size_.height()); - return; - } - - // Failed to resize, fall through and start from scratch - fprintf(stderr, "Failed to resize buffer from %dx%d to %dx%d", - size_.width(), size_.height(), new_size.width(), new_size.height()); - - is_context_bound_ = false; - } - - printf("Calling create context....\n"); - size_ = new_size; - device_context_ = pp::Graphics3D(this, PPAPIGet3DAttribs(size_.width(), - size_.height())); - printf("Got Context!\n"); - is_context_bound_ = BindGraphics(device_context_); - printf("Context is bound=%d\n", is_context_bound_); - - // Set the context regardless to make sure we have a valid one - glSetCurrentContextPPAPI(device_context_.pp_resource()); - if (is_context_bound_) { - PPAPIBuildContext(size_.width(), size_.height()); - device_context_.SwapBuffers(callback_factory_.NewCallback( - &PPAPIInstance::Flushed)); - } else { - fprintf(stderr, "Failed to bind context for %dx%d.\n", size_.width(), - size_.height()); - } -} - -// The default implementation calls the 'C' render function. -void PPAPIInstance3D::Render(PP_Resource ctx, uint32_t width, - uint32_t height) { - PPAPIRender(ctx, width, height); -} - -PPAPIInstance3D::PPAPIInstance3D(PP_Instance instance, const char *args[]) - : PPAPIInstance(instance, args) { - glInitializePPAPI(pp::Module::Get()->get_browser_interface()); -} - - -PPAPIInstance3D::~PPAPIInstance3D() { - if (is_context_bound_) { - is_context_bound_ = false; - // Cleanup code? - } -} diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance3d.h b/native_client_sdk/src/libraries/ppapi_main/ppapi_instance3d.h deleted file mode 100644 index 410b952170..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_instance3d.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PPAPI_MAIN_PPAPI_INSTANCE3D_H_ -#define PPAPI_MAIN_PPAPI_INSTANCE3D_H_ - -#include <map> - -#include "ppapi/c/pp_instance.h" -#include "ppapi/c/pp_resource.h" - -#include "ppapi/cpp/graphics_3d.h" -#include "ppapi/cpp/instance.h" -#include "ppapi/cpp/size.h" - -#include "ppapi_main/ppapi_instance.h" - - -class PPAPIInstance3D : public PPAPIInstance { - public: - PPAPIInstance3D(PP_Instance instance, const char *args[]); - virtual ~PPAPIInstance3D(); - - // Called when we need to rebuild the context - virtual void BuildContext(int32_t result, const pp::Size& new_size); - - // Called whenever a swap takes place - virtual void Flushed(int result); - - virtual void Render(PP_Resource ctx, uint32_t width, uint32_t height); - static PPAPIInstance3D* GetInstance3D(); - - protected: - pp::Graphics3D device_context_; - -}; - - -#endif // PPAPI_MAIN_PPAPI_INSTANCE3D_H_ diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_main.cc b/native_client_sdk/src/libraries/ppapi_main/ppapi_main.cc deleted file mode 100644 index 3d666c910f..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_main.cc +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "ppapi/c/pp_instance.h" -#include "ppapi/c/pp_module.h" - -#include "ppapi/cpp/instance.h" -#include "ppapi/cpp/module.h" - -#include "ppapi_main/ppapi_instance.h" -#include "ppapi_main/ppapi_instance3d.h" -#include "ppapi_main/ppapi_main.h" - - -static pp::Instance* s_Instance = NULL; - - -// Helpers to access PPAPI interfaces -void* PPAPI_GetInstanceObject() { - return s_Instance; -} - -PP_Instance PPAPI_GetInstanceId() { - return s_Instance->pp_instance(); -} - -const void* PPAPI_GetInterface(const char *name) { - return pp::Module::Get()->GetBrowserInterface(name); -} - -void* PPAPI_CreateInstance(PP_Instance inst, const char *args[]) { - return static_cast<void*>(new PPAPIInstance(inst, args)); -} - -PPAPIEvent* PPAPI_AcquireEvent() { - return static_cast<PPAPIInstance*>(s_Instance)->AcquireInputEvent(); -} - -void PPAPI_ReleaseEvent(PPAPIEvent* event) { - static_cast<PPAPIInstance*>(s_Instance)->ReleaseInputEvent(event); -} - - -/// The Module class. The browser calls the CreateInstance() method to create -/// an instance of your NaCl module on the web page. The browser creates a new -/// instance for each <embed> tag with type="application/x-nacl". -class PPAPIMainModule : public pp::Module { - public: - PPAPIMainModule() : pp::Module() {} - virtual ~PPAPIMainModule() {} - - virtual pp::Instance* CreateInstance(PP_Instance instance) { - s_Instance = static_cast<pp::Instance*>(UserCreateInstance(instance)); - return s_Instance; - } -}; - -namespace pp { -/// Factory function called by the browser when the module is first loaded. -/// The browser keeps a singleton of this module. It calls the -/// CreateInstance() method on the object you return to make instances. There -/// is one instance per <embed> tag on the page. This is the main binding -/// point for your NaCl module with the browser. -Module* CreateModule() { - return new PPAPIMainModule(); -} - -} // namespace pp - diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_main.h b/native_client_sdk/src/libraries/ppapi_main/ppapi_main.h deleted file mode 100644 index 2f2c4e31d8..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_main.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PPAPI_MAIN_PPAPI_MAIN_H_ -#define PPAPI_MAIN_PPAPI_MAIN_H_ - -#include "ppapi/c/pp_instance.h" -#include "ppapi/c/pp_module.h" -#include "ppapi_main/ppapi_event.h" - -#include "utils/macros.h" - -EXTERN_C_BEGIN - -// Prototype for 'main' which will get called on startup -int ppapi_main(int argc, const char *argv[]); - -// Provided by "main" module, use one of the macros below -void* UserCreateInstance(PP_Instance inst); - -// Helpers to access PPAPI interfaces -void* PPAPI_GetInstanceObject(); -PP_Instance PPAPI_GetInstanceId(); -const void* PPAPI_GetInterface(const char *name); - -// Provided by library for basic instance types -void* PPAPI_CreateInstance(PP_Instance inst, const char *args[]); -void* PPAPI_CreateInstance2D(PP_Instance inst, const char *args[]); -void* PPAPI_CreateInstance3D(PP_Instance inst, const char *args[]); - -// Event APIs -PPAPIEvent* PPAPI_AcquireEvent(); -void PPAPI_ReleaseEvent(PPAPIEvent* event); - -// Functions for Graphic Instances -int32_t *PPAPIGet3DAttribs(uint32_t width, uint32_t height); -void PPAPIBuildContext(uint32_t width, uint32_t height); -void PPAPIRender(PP_Resource ctx, uint32_t width, uint32_t height); - - -EXTERN_C_END - -#define PPAPI_MAIN_DEFAULT_ARGS NULL, NULL - -#define PPAPI_MAIN_USE(factory, ...) \ -void* UserCreateInstance(PP_Instance inst) { \ - static const char *params[] = { __VA_ARGS__ }; \ - return factory(inst, params); \ -} - -#define PPAPI_MAIN_WITH_DEFAULT_ARGS \ - PPAPI_MAIN_USE(PPAPI_CreateInstance, PPAPI_MAIN_DEFAULT_ARGS) - -#define PPAPI_MAIN_3D_WITH_DEFAULT_ARGS \ - PPAPI_MAIN_USE(PPAPI_CreateInstance3D, PPAPI_MAIN_DEFAULT_ARGS) - -#define PPAPI_MAIN_WITH_ARGS(...) \ - PPAPI_MAIN_USE(PPAPI_CreateInstance, __VA_ARGS__) - - -#endif // PPAPI_MAIN_PPAPI_MAIN_H_ diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_queue.cc b/native_client_sdk/src/libraries/ppapi_main/ppapi_queue.cc deleted file mode 100644 index 9ecce6680e..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_queue.cc +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <assert.h> -#include <stdlib.h> -#include <string.h> - -#include "ppapi_main/ppapi_queue.h" - -PPAPIQueue::PPAPIQueue() - : read_(0), - write_(0), - freed_(0), - size_(0), - array_(NULL) { } - -PPAPIQueue::~PPAPIQueue() { - // Messages may be leaked if the queue is not empty. - assert(read_ == write_); - - delete[] array_; -} - -bool PPAPIQueue::SetSize(uint32_t queue_size) { - assert(queue_size > 0); - - if (array_) return false; - - array_ = new void*[queue_size]; - size_ = queue_size; - - memset(array_, 0, sizeof(void*) * queue_size); - return true; -} - -bool PPAPIQueue::AddNewMessage(void* msg) { - // Writing a NULL message is illegal - assert(array_ != NULL); - assert(msg != NULL); - - // If the slot not empty, the queue must be full. Calling RemoveStaleMessage - // may create space by freeing messages that have already been read. - if (array_[write_] != NULL) return false; - - // Write to the spot - array_[write_] = msg; - - // Fence to make sure the payload and payload pointer are visible. - // Since Win32 is x86 which provides ordered writes, we don't need to - // synchronize in that case. -#ifndef WIN32 - __sync_synchronize(); -#endif - - // Increment the write pointer, to signal it's readable. - write_ = (write_ + 1) % size_; - return true; -} - -void* PPAPIQueue::RemoveStaleMessage() { - assert(array_ != NULL); - - // If freed and read pointer are equal, this hasn't been read yet - if (freed_ == read_) return NULL; - - assert(array_[freed_] != NULL); - - void* ret = array_[freed_]; - array_[freed_] = NULL; - - freed_ = (freed_ + 1) % size_; - return ret; -} - -void* PPAPIQueue::AcquireTopMessage() { - // Assert that we aren't already reading a message. - assert(last_msg_ == NULL); - - // If read and write pointers are equal, the queue is empty. - if (read_ == write_) return NULL; - - // Track the last message to look for illegal API use. - last_msg_ = array_[read_]; - return last_msg_; -} - -void PPAPIQueue::ReleaseTopMessage(void* msg) { - // Verify we currently acquire message. - assert(msg != NULL); - assert(msg == last_msg_); - - last_msg_ = NULL; - - // Signal that the message can be freed. - read_ = (read_ + 1) % size_; -} diff --git a/native_client_sdk/src/libraries/ppapi_main/ppapi_queue.h b/native_client_sdk/src/libraries/ppapi_main/ppapi_queue.h deleted file mode 100644 index f022dcf0b3..0000000000 --- a/native_client_sdk/src/libraries/ppapi_main/ppapi_queue.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -#ifndef PPAPI_MAIN_PPAPI_QUEUE_H -#define PPAPI_MAIN_PPAPI_QUEUE_H - -#include <stdint.h> - -// -// PPAPIQueue -// -// PPAPIQueue is a single producer/single consumer lockless queue. This -// implementation is PPAPI friendly because it prevents the main thread from -// ever needing to lock. In addition, allocation and destruction of messages -// happens on the same thread, allowing for lockless message allocation as well. -// -// Messages pass through four states in order: -// AddNewMessage - The message is added to the queue by the producer -// and the memory is fenced to ensure it is visible. Once the fence -// returns, the write pointer is incremented to signal it's available to -// the consumer. NOTE: NULL messages are illegal. -// -// AcquireTopMessage - Next the message is acquired by the consumer thread. -// At this point, the consumer is considered to be examining the payload so -// we do not increment the read pointer yet to prevent deletion. NOTE: it -// is illegal to acquire the next message until the previous one is released. -// -// ReleaseTopMessage - Now the consumer signals that it is no longer looking -// at the message by incremented the read pointer. The producer is free to -// release or reuse the payload. -// -// RemoveStaleMessage - The message is no longer visible to the consumer, so -// it is returned to the producer to be reused or destroyed. It's location -// in the queue is set to NULL to signal that a new message may be added in -// that slot. -// -class PPAPIQueue { - public: - PPAPIQueue(); - ~PPAPIQueue(); - - // - // Producer API - // - // First, the producer must set the queue size before any operation can - // take place. Next, before adding a message, clear space in the queue, - // by removing stale messages. Adding a new message will return TRUE if - // space is available, otherwise FALSE is returned and it's up to the - // application developer to decide what to do. - // - bool SetSize(uint32_t queue_size); - bool AddNewMessage(void* msg); - void* RemoveStaleMessage(); - - // - // Consumer API - // - // The reader will attempt to Acquire the top message, if one is not - // available NULL will be returned. Once the consumer is done with the - // message, ReleaseTopMessage is called to signal that the payload is no - // longer visible to the consumer and can be recycled or destroyed. - // Since messages are freed in order, it is required that messages are - // consumed in order. For this reason, it is illegal to call Acquire again - // after a non-NULL message pointer is returned, until Release is called on - // the old message. This means the consumer can only look at one message - // at a time. To look at multiple messages at once, the consumer would - // need to make a copy and release the top message. - // - void* AcquireTopMessage(); - void ReleaseTopMessage(void* msg); - - private: - uint32_t read_; - uint32_t write_; - uint32_t freed_; - uint32_t size_; - void* last_msg_; - void** array_; -}; - - -#endif // PPAPI_MAIN_PPAPI_QUEUE_H diff --git a/native_client_sdk/src/libraries/ppapi_simple/library.dsc b/native_client_sdk/src/libraries/ppapi_simple/library.dsc new file mode 100644 index 0000000000..613a585227 --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/library.dsc @@ -0,0 +1,28 @@ +{ + 'TOOLS': ['newlib', 'glibc', 'pnacl'], + 'TARGETS': [ + { + 'NAME' : 'ppapi_simple', + 'TYPE' : 'lib', + 'SOURCES' : [ + "ps.cc", + "ps_event.cc", + "ps_instance.cc", + "ps_main.cc", + ], + } + ], + 'HEADERS': [ + { + 'FILES': [ + "ps.h", + "ps_event.h", + "ps_instance.h", + "ps_main.h", + ], + 'DEST': 'include/ppapi_simple', + }, + ], + 'DEST': 'src', + 'NAME': 'ppapi_simple', +} diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps.cc b/native_client_sdk/src/libraries/ppapi_simple/ps.cc new file mode 100644 index 0000000000..eba3c4030b --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps.cc @@ -0,0 +1,51 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_module.h" + +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/module.h" + +#include "ppapi_simple/ps.h" + +static pp::Instance* s_Instance = NULL; + +PP_Instance PSGetInstanceId() { + return s_Instance->pp_instance(); +} + +const void* PSGetInterface(const char *name) { + return pp::Module::Get()->GetBrowserInterface(name); +} + + +// The Module class. The browser calls the CreateInstance() method to create +// an instance of your NaCl module on the web page. The browser creates a new +// instance for each <embed> tag with type="application/x-nacl". +class PSModule : public pp::Module { + public: + PSModule() : pp::Module() {} + virtual ~PSModule() {} + + virtual pp::Instance* CreateInstance(PP_Instance instance) { + s_Instance = static_cast<pp::Instance*>(PSUserCreateInstance(instance)); + return s_Instance; + } +}; + +namespace pp { + +// Factory function called by the browser when the module is first loaded. +// The browser keeps a singleton of this module. It calls the +// CreateInstance() method on the object you return to make instances. There +// is one instance per <embed> tag on the page. This is the main binding +// point for your NaCl module with the browser. +Module* CreateModule() { + return new PSModule(); +} + +} // namespace pp + diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps.h b/native_client_sdk/src/libraries/ppapi_simple/ps.h new file mode 100644 index 0000000000..1d7b2543ae --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps.h @@ -0,0 +1,113 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PPAPI_SIMPLE_PS_H_ +#define PPAPI_SIMPLE_PS_H_ + +#include "ppapi/c/pp_instance.h" +#include "sdk_util/macros.h" + +EXTERN_C_BEGIN + +/** + * The ppapi_simple library simplifies the use of the Pepper interfaces by + * providing a more traditional 'C' or 'C++' style framework. The library + * creates an PSInstance derived object based on the ppapi_cpp library and + * initializes the nacl_io library to provide a POSIX friendly I/O environment. + * + * In order to provide a standard blocking environment, the library will hide + * the actual "Pepper Thread" which is the thread that standard events + * such as window resize, mouse keyboard, or other inputs arrive. To prevent + * blocking, instead we enqueue these events onto a thread safe linked list + * and expect them to be processed on a new thread. In addition, the library + * will automatically start a new thread on which can be used effectively + * as a "main" entry point. + * + * For C style development, the PPAPI_SIMPLE_REGISTER_MAIN(XX) macros provide a + * mechanism to register the entry an point for "main". All events are pushed + * onto an event queue which can then be pulled from this new thread. + * NOTE: The link will still need libstdc++ and libppapi_cpp since the library + * is still creating a C++ object which does the initialization work and + * forwards the events. + * + * For C++ style development, use the ppapi_simple_instance.h, + * ppapi_simple_instance_2d.h, and ppapi_simple_instance_3d.h headers as + * a base class, and overload the appropriate virtual functions such as + * Main, ChangeContext, or Render. + */ + +/** + * PSGetInstanceId + * + * Return the PP_Instance id of this instance of the module. This is required + * by most of the Pepper resource creation routines. + */ +PP_Instance PSGetInstanceId(); + + +/** + * PSGetInterface + * + * Return the Pepper instance referred to by 'name'. Will return a pointer + * to the interface, or NULL if not found or not available. + */ +const void* PSGetInterface(const char *name); + + +/** + * PSUserCreateInstance + * + * Prototype for the user provided function which creates and configures + * the instance object. This function is defined by one of the macros below, + * or by the equivalent macro in one of the other headers. For 'C' + * development, one of the basic instances which support C callback are used. + * For C++, this function should instantiate the user defined instance. See + * the two macros below. + */ +extern void* PSUserCreateInstance(PP_Instance inst); + + +/** + * PPAPI_SIMPLE_DECLARE_PARAMS + * + * Macro for creating the param string array. Used by the Factory macros + * to enable wrapping of the param array with terminating NULL, NULL. + */ +#define PPAPI_SIMPLE_DECLARE_PARAMS(params, ...) \ + static const char* params[] = { __VA_ARGS__ }; + + +/** + * PPAPI_SIMPLE_USE_MAIN + * + * For use with C projects, this macro calls the provided factory with + * configuration information and optional extra arguments which are passed to + * the 'main' function. See the appropriate ppapi_simple_main(XX).h header. + */ +#define PPAPI_SIMPLE_USE_MAIN(factory, func, ...) \ +void* PSUserCreateInstance(PP_Instance inst) { \ + PPAPI_SIMPLE_DECLARE_PARAMS(params, ##__VA_ARGS__, NULL, NULL) \ + return factory(inst, func, params); \ +} + + +/** + * PPAPI_SIMPLE_USE_CLASS + * + * For use with C++ projects, this macro instantiates the provided class + * passing it back to the generic module initialization code. Simply derive + * a class from the appropriate ppapi_simple_instance(XX).h and pass the + * class name here. + */ +#define PPAPI_SIMPLE_USE_CLASS(classname, ...) \ +void* PSUserCreateInstance(PP_Instance inst) { \ + PPAPI_SIMPLE_DECLARE_PARAMS(params, ##__VA_ARGS__, NULL, NULL) \ + return (void *) new classname(inst, params); \ +} + +EXTERN_C_END + + +#endif // PPAPI_SIMPLE_PPAPI_SIMPLE_H + diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_event.cc b/native_client_sdk/src/libraries/ppapi_simple/ps_event.cc new file mode 100644 index 0000000000..ac68fe9b59 --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_event.cc @@ -0,0 +1,45 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_module.h" + +#include "ppapi_simple/ps_event.h" +#include "ppapi_simple/ps_instance.h" +#include "ppapi_simple/ps_main.h" + + +void PSEventPost(PSEventType type) { + PSInstance::GetInstance()->PostEvent(type); +} + +void PSEventPostBool(PSEventType type, PP_Bool state) { + PSInstance::GetInstance()->PostEvent(type, state); +} + +void PSEventPostVar(PSEventType type, struct PP_Var var) { + PSInstance::GetInstance()->PostEvent(type, var); +} + +void PSEventPostResource(PSEventType type, PP_Resource resource) { + PSInstance::GetInstance()->PostEvent(type, resource); +} + +PSEvent* PSEventTryAcquire() { + return PSInstance::GetInstance()->TryAcquireEvent(); +} + +PSEvent* PSEventWaitAcquire() { + return PSInstance::GetInstance()->WaitAcquireEvent(); +} + +void PSEventRelease(PSEvent* event) { + PSInstance::GetInstance()->ReleaseEvent(event); +} + +void PSEventSetFilter(PSEventTypeMask filter) { + PSInstance::GetInstance()->SetEnabledEvents(filter); +} + diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_event.h b/native_client_sdk/src/libraries/ppapi_simple/ps_event.h new file mode 100644 index 0000000000..563832c703 --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_event.h @@ -0,0 +1,95 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#ifndef PPAPI_SIMPLE_PS_EVENT_H_ +#define PPAPI_SIMPLE_PS_EVENT_H_ + +#include "ppapi/c/pp_bool.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/pp_var.h" + +#include "sdk_util/macros.h" + +EXTERN_C_BEGIN + +/** + * PSEvent + * + * The PSEvent system provides an in-order event processing mechanism. + * As Pepper events such as input, or focus and view changes arrive on the + * main pepper thread, they are placed on various FIFOs based on event type. + * For any given event type, only a single thread will process those events. + * + * By default, the "EventThread" will receive all messages. However any thread + * can call PSRequestEventsType to request that all new events of that type + * are placed on that particular thread's queue. + * + * This allows the developer to choose which threads handle which event types + * while keeping all events belonging to a particular thread in order. This + * is useful, for example, in the case of graphics to synchronize the size + * of the graphics context, with mouse clicks to correctly interpret location. + */ + + +typedef enum { + /* Mask off all events. */ + PSE_NONE = 0, + + /* From HandleInputEvent, conatins an input resource. */ + PSE_INSTANCE_HANDLEINPUT = 1, + + /* From HandleMessage, contains a PP_Var. */ + PSE_INSTANCE_HANDLEMESSAGE = 2, + + /* From DidChangeView, contains a view resource */ + PSE_INSTANCE_DIDCHANGEVIEW = 4, + + /* From DidChangeFocus, contains a PP_Bool with the current focus state. */ + PSE_INSTANCE_DIDCHANGEFOCUS = 8, + + /* When the 3D context is lost, no resource. */ + PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST = 16, + + /* When the mouse lock is lost. */ + PSE_MOUSELOCK_MOUSELOCKLOST = 32, + + /* Enable all events. */ + PSE_ALL = -1, +} PSEventType; + +typedef uint32_t PSEventTypeMask; + +// Generic Event +typedef struct { + PSEventType type; + union { + PP_Bool as_bool; + PP_Resource as_resource; + struct PP_Var as_var; + }; +} PSEvent; + + +/** + * Function for queuing, acquiring, and releasing events. + */ +PSEvent* PSEventTryAcquire(); +PSEvent* PSEventWaitAcquire(); +void PSEventRelease(PSEvent* event); +void PSEventSetFilter(PSEventTypeMask mask); + +/** + * Creates and adds an event of the specified type to the event queue if + * that event type is not currently filtered. + */ +void PSEventPost(PSEventType type); +void PSEventPostBool(PSEventType type, PP_Bool state); +void PSEventPostVar(PSEventType type, struct PP_Var var); +void PSEventPostResource(PSEventType type, PP_Resource resource); + +EXTERN_C_END + +#endif // PPAPI_SIMPLE_PS_EVENT_H_ + diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc new file mode 100644 index 0000000000..120378765f --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc @@ -0,0 +1,357 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <fcntl.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <cstdlib> +#include <cstring> +#include <map> +#include <string> +#include <vector> + +#include "nacl_io/kernel_wrap.h" +#include "nacl_io/nacl_io.h" + +#include "ppapi/c/ppb_var.h" + +#include "ppapi/cpp/input_event.h" +#include "ppapi/cpp/message_loop.h" +#include "ppapi/cpp/module.h" +#include "ppapi/cpp/rect.h" +#include "ppapi/cpp/size.h" +#include "ppapi/cpp/touch_point.h" +#include "ppapi/cpp/var.h" + +#include "ppapi_simple/ps_event.h" +#include "ppapi_simple/ps_instance.h" +#include "ppapi_simple/ps_main.h" + +static PSInstance* s_InstanceObject = NULL; + +PSInstance* PSInstance::GetInstance() { + return s_InstanceObject; +} + +struct StartInfo { + PSInstance* inst_; + uint32_t argc_; + const char** argv_; +}; + + +// The starting point for 'main'. We create this thread to hide the real +// main pepper thread which must never be blocked. +void* PSInstance::MainThreadThunk(void *info) { + s_InstanceObject->Log("Got MainThreadThunk.\n"); + StartInfo* si = static_cast<StartInfo*>(info); + si->inst_->main_loop_ = new pp::MessageLoop(si->inst_); + si->inst_->main_loop_->AttachToCurrentThread(); + + int ret = si->inst_->MainThread(si->argc_, si->argv_); + for (uint32_t i = 0; i < si->argc_; i++) { + delete[] si->argv_[i]; + } + delete[] si->argv_; + delete si; + + return NULL; +} + +// The default implementation supports running a 'C' main. +int PSInstance::MainThread(int argc, const char *argv[]) { + if (main_cb_) { + Log("Starting MAIN.\n"); + int ret = main_cb_(argc, argv); + Log("Main thread returned with %d.\n", ret); + return ret; + } + + Error("No main defined.\n"); + return 0; +} + +PSInstance::PSInstance(PP_Instance instance, const char *argv[]) + : pp::Instance(instance), + main_loop_(NULL), + verbosity_(PSV_LOG), + events_enabled_(PSE_NONE) { + // Set the single Instance object + s_InstanceObject = this; + +#ifdef DEBUG + SetVerbosity(PSV_LOG); +#endif + + // Place PPAPI_MAIN_USE arguments into properties map + while (*argv) { + std::string key = *argv++; + std::string val = *argv++; + properties_[key] = val; + } + + RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | + PP_INPUTEVENT_CLASS_KEYBOARD | + PP_INPUTEVENT_CLASS_WHEEL | + PP_INPUTEVENT_CLASS_TOUCH); + + ppb_core_ = static_cast<const PPB_Core*>( + pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE)); + + ppb_var_ = static_cast<const PPB_Var*>( + pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE)); + + ppb_view_ = static_cast<const PPB_View*>( + pp::Module::Get()->GetBrowserInterface(PPB_VIEW_INTERFACE)); +} + +PSInstance::~PSInstance() {} + +void PSInstance::SetMain(PSMainFunc_t main) { + main_cb_ = main; +} + +bool PSInstance::Init(uint32_t arg, + const char* argn[], + const char* argv[]) { + StartInfo* si = new StartInfo; + + si->inst_ = this; + si->argc_ = 1; + si->argv_ = new const char *[arg*2+1]; + si->argv_[0] = NULL; + + // Process arguments passed into Module INIT from JavaScript + for (uint32_t i=0; i < arg; i++) { + if (argv[i]) { + Log("ARG %s=%s\n", argn[i], argv[i]); + } else { + Log("ARG %s\n", argn[i]); + } + + // If we start with PM prefix set the instance argument map + if (0 == strncmp(argn[i], "ps_", 3)) { + std::string key = argn[i]; + std::string val = argv[i]; + properties_[key] = val; + continue; + } + + // If this is the 'src' tag, then get the NMF name. + if (!strcmp("src", argn[i])) { + char *name = new char[strlen(argv[i]) + 1]; + strcpy(name, argv[i]); + si->argv_[0] = name; + } + else { + // Otherwise turn it into arguments + char *key = new char[strlen(argn[i]) + 3]; + key[0] = '-'; + key[1] = '-'; + strcpy(&key[2], argn[i]); + + si->argv_[si->argc_++] = key; + if (argv[i] && argv[i][0]) { + char *val = new char[strlen(argv[i]) + 1]; + strcpy(val, argv[i]); + si->argv_[si->argc_++] = val; + } + } + } + + // If src was not found, set the first arg to something + if (NULL == si->argv_[0]) { + char *name = new char[5]; + strcpy(name, "NMF?"); + si->argv_[0] = name; + } + + if (ProcessProperties()) { + pthread_t main_thread; + int ret = pthread_create(&main_thread, NULL, MainThreadThunk, si); + Log("Created thread: %d.\n", ret); + return ret == 0; + } + + Log("Skipping create thread.\n"); + return false; +} + +const char* PSInstance::GetProperty(const char* key, const char* def) { + PropertyMap_t::iterator it = properties_.find(key); + if (it != properties_.end()) { + return it->second.c_str(); + } + return def; +} + +// Processes the properties passed fixed at compile time via the +// initialization macro, or via dynamically set embed attributes +// through instance DidCreate. +bool PSInstance::ProcessProperties() { + // Get the default values + const char* stdin_path = GetProperty("ps_stdin", "/dev/stdin"); + const char* stdout_path = GetProperty("ps_stdout", "/dev/stdout"); + const char* stderr_path = GetProperty("ps_stderr", "/dev/console3"); + const char* verbosity = GetProperty("ps_verbosity", NULL); + + // Reset verbosity if passed in + if (verbosity) SetVerbosity(static_cast<Verbosity>(atoi(verbosity))); + + // Enable NaCl IO to map STDIN, STDOUT, and STDERR + nacl_io_init_ppapi(PSGetInstanceId(), PSGetInterface); + int fd0 = open(stdin_path, O_RDONLY); + dup2(fd0, 0); + + int fd1 = open(stdout_path, O_WRONLY); + dup2(fd1, 1); + + int fd2 = open(stderr_path, O_WRONLY); + dup2(fd2, 2); + + // Set line buffering on stdout and stderr + setvbuf(stderr, NULL, _IOLBF, 0); + setvbuf(stdout, NULL, _IOLBF, 0); + return true; +} + +void PSInstance::SetVerbosity(Verbosity verbosity) { + verbosity_ = verbosity; +} + +void PSInstance::Log(const char *fmt, ...) { + if (verbosity_ >= PSV_LOG) { + va_list ap; + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + } +} + +void PSInstance::Warn(const char *fmt, ...) { + if (verbosity_ >= PSV_WARN) { + va_list ap; + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + } +} + +void PSInstance::Error(const char *fmt, ...) { + if (verbosity_ >= PSV_ERROR) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + } +} + +void PSInstance::SetEnabledEvents(uint32_t mask) { + events_enabled_ = mask; +} + +void PSInstance::PostEvent(PSEventType type) { + assert(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST == type || + PSE_MOUSELOCK_MOUSELOCKLOST == type); + + if (events_enabled_ & type) { + PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent)); + memset(env, 0, sizeof(*env)); + env->type = type; + event_queue_.Enqueue(env); + } +} + +void PSInstance::PostEvent(PSEventType type, PP_Bool bool_value) { + assert(PSE_INSTANCE_DIDCHANGEFOCUS == type); + + if (events_enabled_ & type) { + PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent)); + memset(env, 0, sizeof(*env)); + env->type = type; + env->as_bool = bool_value; + event_queue_.Enqueue(env); + } +} + +void PSInstance::PostEvent(PSEventType type, PP_Resource resource) { + assert(PSE_INSTANCE_HANDLEINPUT == type || + PSE_INSTANCE_DIDCHANGEVIEW == type); + + if (events_enabled_ & type) { + if (resource) { + ppb_core_->AddRefResource(resource); + } + PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent)); + memset(env, 0, sizeof(*env)); + env->type = type; + env->as_resource = resource; + event_queue_.Enqueue(env); + } +} + +void PSInstance::PostEvent(PSEventType type, const PP_Var& var) { + assert(PSE_INSTANCE_HANDLEMESSAGE == type); + + if (events_enabled_ & type) { + ppb_var_->AddRef(var); + PSEvent *env = (PSEvent *) malloc(sizeof(PSEvent)); + memset(env, 0, sizeof(*env)); + env->type = type; + env->as_var = var; + event_queue_.Enqueue(env); + } +} + +PSEvent* PSInstance::TryAcquireEvent() { + return event_queue_.Dequeue(false); +} + +PSEvent* PSInstance::WaitAcquireEvent() { + return event_queue_.Dequeue(true); +} + +void PSInstance::ReleaseEvent(PSEvent* event) { + if (event) { + switch(event->type) { + case PSE_INSTANCE_HANDLEMESSAGE: + ppb_var_->Release(event->as_var); + break; + case PSE_INSTANCE_HANDLEINPUT: + case PSE_INSTANCE_DIDCHANGEVIEW: + if (event->as_resource) { + ppb_core_->ReleaseResource(event->as_resource); + } + break; + default: + break; + } + free(event); + } +} + +void PSInstance::HandleMessage(const pp::Var& message) { + Log("Got Message\n"); + PostEvent(PSE_INSTANCE_HANDLEMESSAGE, message.pp_var()); +} + +bool PSInstance::HandleInputEvent(const pp::InputEvent& event) { + Log("Got Input\n"); + PostEvent(PSE_INSTANCE_HANDLEINPUT, event.pp_resource()); + return true; +} + +void PSInstance::DidChangeView(const pp::View& view) { + pp::Size new_size = view.GetRect().size(); + Log("Got View change: %d,%d\n", new_size.width(), new_size.height()); + PostEvent(PSE_INSTANCE_DIDCHANGEVIEW, view.pp_resource()); +} + +void PSInstance::DidChangeFocus(bool focus) { + Log("Got Focus change: %s\n", focus ? "FOCUS ON" : "FOCUS OFF"); + PostEvent(PSE_INSTANCE_DIDCHANGEFOCUS, focus ? PP_TRUE : PP_FALSE); +} + diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h new file mode 100644 index 0000000000..d888dd7044 --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h @@ -0,0 +1,121 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PPAPI_SIMPLE_PS_INSTANCE_H_ +#define PPAPI_SIMPLE_PS_INSTANCE_H_ + +#include <map> + +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_stdint.h" +#include "ppapi/c/ppb_core.h" +#include "ppapi/c/ppb_var.h" +#include "ppapi/c/ppb_view.h" + +#include "ppapi/cpp/fullscreen.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/message_loop.h" +#include "ppapi/cpp/mouse_lock.h" + +#include "ppapi/utility/completion_callback_factory.h" + +#include "ppapi_simple/ps_event.h" +#include "ppapi_simple/ps_main.h" + +#include "sdk_util/thread_safe_queue.h" + + +typedef std::map<std::string, std::string> PropertyMap_t; + +class PSInstance : public pp::Instance { + public: + enum Verbosity { + PSV_SILENT, + PSV_ERROR, + PSV_WARN, + PSV_LOG, + }; + + // Returns a pointer to the global instance + static PSInstance* GetInstance(); + + PSInstance(PP_Instance inst, const char *argv[]); + virtual ~PSInstance(); + + // Set a function which will be called on a new thread once initialized. + // NOTE: This must be called by the Factory, once Init is called, this + // function will have no effect. + void SetMain(PSMainFunc_t func); + + // Returns value based on KEY or default. + const char* GetProperty(const char* key, const char* def = NULL); + + // Started on Init, a thread which can be safely blocked. + virtual int MainThread(int argc, const char* argv[]); + + // Logging Functions + void SetVerbosity(Verbosity verbosity); + void Log(const char *fmt, ...); + void Warn(const char *fmt, ...); + void Error(const char *fmt, ...); + + // Event Functions + void SetEnabledEvents(uint32_t mask); + void PostEvent(PSEventType type); + void PostEvent(PSEventType type, PP_Bool bool_value); + void PostEvent(PSEventType type, PP_Resource resource); + void PostEvent(PSEventType type, const PP_Var& var); + + PSEvent* TryAcquireEvent(); + PSEvent* WaitAcquireEvent(); + void ReleaseEvent(PSEvent* event); + + protected: + // Callback functions triggered by Pepper + // + // These functions are called on the main pepper thread, so they must + // not block. + // + // Called by the browser when the NaCl module is loaded and all ready to go. + // This function will create a new thread which will run the pseudo main. + virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); + + // Called whenever the in-browser window changes size, it will pass a + // context change request to whichever thread is handling rendering. + virtual void DidChangeView(const pp::View& view); + + // Called by the browser when the NaCl canvas gets or loses focus. + virtual void DidChangeFocus(bool has_focus); + + // Called by the browser to handle the postMessage() call in Javascript. + virtual void HandleMessage(const pp::Var& message); + + // Called by the browser to handle incoming input events. Events are Q'd + // and can later be processed on a sperate processing thread. + virtual bool HandleInputEvent(const pp::InputEvent& event); + + // Called by Init to processes default and embed tag arguments prior to + // launching the 'ppapi_main' thread. + virtual bool ProcessProperties(); + + private: + static void* MainThreadThunk(void *start_info); + + protected: + pp::MessageLoop* main_loop_; + + PropertyMap_t properties_; + ThreadSafeQueue<PSEvent> event_queue_; + uint32_t events_enabled_; + Verbosity verbosity_; + + PSMainFunc_t main_cb_; + + const PPB_Core* ppb_core_; + const PPB_Var* ppb_var_; + const PPB_View* ppb_view_; +}; + +#endif // PPAPI_MAIN_PS_INSTANCE_H_ + diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_main.cc b/native_client_sdk/src/libraries/ppapi_simple/ps_main.cc new file mode 100644 index 0000000000..302aeaf44b --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_main.cc @@ -0,0 +1,17 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_module.h" + +#include "ppapi_simple/ps_instance.h" +#include "ppapi_simple/ps_main.h" + + +void* PSMainCreate(PP_Instance inst, PSMainFunc_t func, const char* argv[]) { + PSInstance* pInst = new PSInstance(inst, argv); + pInst->SetMain(func); + return pInst; +} diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_main.h b/native_client_sdk/src/libraries/ppapi_simple/ps_main.h new file mode 100644 index 0000000000..6bd66e291a --- /dev/null +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_main.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PPAPI_SIMPLE_PS_MAIN_H_ +#define PPAPI_SIMPLE_PS_MAIN_H_ + +#include "ppapi_simple/ps.h" +#include "ppapi_simple/ps_event.h" + +EXTERN_C_BEGIN + +typedef int (*PSMainFunc_t)(int argc, const char *argv[]); + +/** + * PSMainCreate + * + * Constructs an instance SimpleInstance and configures it to call into + * the provided "main" function. + */ +void* PSMainCreate(PP_Instance inst, PSMainFunc_t func, const char **argv); + + +/** + * PPAPI_SIMPLE_REGISTER_MAIN + * + * Constructs a PSInstance ojbect and configures it to use call the provided + * 'main' function on it's own thread once initialization is complete. + */ + + +#define PPAPI_SIMPLE_REGISTER_MAIN(main, ...) \ + PPAPI_SIMPLE_USE_MAIN(PSMainCreate, main, ##__VA_ARGS__) + +EXTERN_C_END + +#endif /* PPAPI_SIMPLE_PPAPI_SIMPLE_MAIN_H_ */ + diff --git a/native_client_sdk/src/libraries/utils/auto_lock.h b/native_client_sdk/src/libraries/sdk_util/auto_lock.h index 19dd66f84d..88215d0cb3 100644 --- a/native_client_sdk/src/libraries/utils/auto_lock.h +++ b/native_client_sdk/src/libraries/sdk_util/auto_lock.h @@ -3,8 +3,8 @@ * found in the LICENSE file. */ -#ifndef LIBRARIES_UTILS_AUTO_LOCK_H_ -#define LIBRARIES_UTILS_AUTO_LOCK_H_ +#ifndef LIBRARIES_SDK_UTIL_AUTO_LOCK_H_ +#define LIBRARIES_SDK_UTIL_AUTO_LOCK_H_ #include <pthread.h> @@ -27,4 +27,5 @@ class AutoLock { pthread_mutex_t* lock_; }; -#endif // LIBRARIES_UTILS_AUTO_LOCK_H_ +#endif // LIBRARIES_SDK_UTIL_AUTO_LOCK_H_ + diff --git a/native_client_sdk/src/libraries/sdk_util/library.dsc b/native_client_sdk/src/libraries/sdk_util/library.dsc new file mode 100644 index 0000000000..0491d980ea --- /dev/null +++ b/native_client_sdk/src/libraries/sdk_util/library.dsc @@ -0,0 +1,29 @@ +{ + 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win'], + 'SEARCH': [ + '.' + ], + 'TARGETS': [ + { + 'NAME' : 'sdk_util', + 'TYPE' : 'lib', + 'SOURCES' : [ + 'thread_pool.cc' + ] + } + ], + 'HEADERS': [ + { + 'FILES': [ + 'auto_lock.h', + 'macros.h', + 'ref_object.h', + 'thread_pool.h', + 'thread_safe_queue.h' + ], + 'DEST': 'include/sdk_util', + } + ], + 'DEST': 'src', + 'NAME': 'sdk_util', +} diff --git a/native_client_sdk/src/libraries/utils/macros.h b/native_client_sdk/src/libraries/sdk_util/macros.h index 718d83fce9..fd15dc38c8 100644 --- a/native_client_sdk/src/libraries/utils/macros.h +++ b/native_client_sdk/src/libraries/sdk_util/macros.h @@ -2,8 +2,8 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#ifndef LIBRARIES_UTILS_MACROS_H_ -#define LIBRARIES_UTILS_MACROS_H_ +#ifndef LIBRARIES_SDK_UTIL_MACROS_H_ +#define LIBRARIES_SDK_UTIL_MACROS_H_ /** * A macro to disallow the evil copy constructor and operator= functions @@ -46,4 +46,5 @@ force_link_##x = 1; \ } -#endif /* LIBRARIES_UTILS_MACROS_H_ */ +#endif /* LIBRARIES_SDK_UTIL_MACROS_H_ */ + diff --git a/native_client_sdk/src/libraries/utils/ref_object.h b/native_client_sdk/src/libraries/sdk_util/ref_object.h index d3eb9f87c4..4e81f32df6 100644 --- a/native_client_sdk/src/libraries/utils/ref_object.h +++ b/native_client_sdk/src/libraries/sdk_util/ref_object.h @@ -3,8 +3,8 @@ * found in the LICENSE file. */ -#ifndef LIBRARIES_UTILS_REF_OBJECT -#define LIBRARIES_UTILS_REF_OBJECT +#ifndef LIBRARIES_SDK_UTIL_REF_OBJECT +#define LIBRARIES_SDK_UTIL_REF_OBJECT #include <stdlib.h> #include "pthread.h" @@ -45,4 +45,5 @@ class RefObject { int ref_count_; }; -#endif // LIBRARIES_UTILS_REF_OBJECT +#endif // LIBRARIES_SDK_UTIL_REF_OBJECT + diff --git a/native_client_sdk/src/libraries/sdk_util/thread_pool.cc b/native_client_sdk/src/libraries/sdk_util/thread_pool.cc new file mode 100644 index 0000000000..e7d2e5cb6b --- /dev/null +++ b/native_client_sdk/src/libraries/sdk_util/thread_pool.cc @@ -0,0 +1,144 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sdk_util/thread_pool.h" + +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> +#include <stdlib.h> + +#include "sdk_util/auto_lock.h" + +// Initializes mutex, semaphores and a pool of threads. If 0 is passed for +// num_threads, all work will be performed on the dispatch thread. +ThreadPool::ThreadPool(int num_threads) + : threads_(NULL), counter_(0), num_threads_(num_threads), exiting_(false), + user_data_(NULL), user_work_function_(NULL) { + if (num_threads_ > 0) { + int status; + status = sem_init(&work_sem_, 0, 0); + if (-1 == status) { + fprintf(stderr, "Failed to initialize semaphore!\n"); + exit(-1); + } + status = sem_init(&done_sem_, 0, 0); + if (-1 == status) { + fprintf(stderr, "Failed to initialize semaphore!\n"); + exit(-1); + } + threads_ = new pthread_t[num_threads_]; + for (int i = 0; i < num_threads_; i++) { + status = pthread_create(&threads_[i], NULL, WorkerThreadEntry, this); + if (0 != status) { + fprintf(stderr, "Failed to create thread!\n"); + exit(-1); + } + } + } +} + +// Post exit request, wait for all threads to join, and cleanup. +ThreadPool::~ThreadPool() { + if (num_threads_ > 0) { + PostExitAndJoinAll(); + delete[] threads_; + sem_destroy(&done_sem_); + sem_destroy(&work_sem_); + } +} + +// Setup work parameters. This function is called from the dispatch thread, +// when all worker threads are sleeping. +void ThreadPool::Setup(int counter, WorkFunction work, void *data) { + counter_ = counter; + user_work_function_ = work; + user_data_ = data; +} + +// Return decremented task counter. This function +// can be called from multiple threads at any given time. +int ThreadPool::DecCounter() { +#if defined(__native_client__) + // Use fast atomic sub & fetch. + return __sync_sub_and_fetch(&counter_, 1); +#else + // Fallback to a more platform independent pthread mutex via AutoLock. + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + AutoLock lock(&mutex); + return --counter_; +#endif +} + +// Set exit flag, post and join all the threads in the pool. This function is +// called only from the dispatch thread, and only when all worker threads are +// sleeping. +void ThreadPool::PostExitAndJoinAll() { + exiting_ = true; + // Wake up all the sleeping worker threads. + for (int i = 0; i < num_threads_; ++i) + sem_post(&work_sem_); + void* retval; + for (int i = 0; i < num_threads_; ++i) + pthread_join(threads_[i], &retval); +} + +// Main work loop - one for each worker thread. +void ThreadPool::WorkLoop() { + while (true) { + // Wait for work. If no work is availble, this thread will sleep here. + sem_wait(&work_sem_); + if (exiting_) break; + while (true) { + // Grab a task index to work on from the counter. + int task_index = DecCounter(); + if (task_index < 0) + break; + user_work_function_(task_index, user_data_); + } + // Post to dispatch thread work is done. + sem_post(&done_sem_); + } +} + +// pthread entry point for a worker thread. +void* ThreadPool::WorkerThreadEntry(void* thiz) { + static_cast<ThreadPool*>(thiz)->WorkLoop(); + return NULL; +} + +// DispatchMany() will dispatch a set of tasks across worker threads. +// Note: This function will block until all work has completed. +void ThreadPool::DispatchMany(int num_tasks, WorkFunction work, void* data) { + // On entry, all worker threads are sleeping. + Setup(num_tasks, work, data); + + // Wake up the worker threads & have them process tasks. + for (int i = 0; i < num_threads_; i++) + sem_post(&work_sem_); + + // Worker threads are now awake and busy. + + // This dispatch thread will now sleep-wait for the worker threads to finish. + for (int i = 0; i < num_threads_; i++) + sem_wait(&done_sem_); + // On exit, all tasks are done and all worker threads are sleeping again. +} + +// DispatchHere will dispatch all tasks on this thread. +void ThreadPool::DispatchHere(int num_tasks, WorkFunction work, void* data) { + for (int i = 0; i < num_tasks; i++) + work(i, data); +} + +// Dispatch() will invoke the user supplied work function across +// one or more threads for each task. +// Note: This function will block until all work has completed. +void ThreadPool::Dispatch(int num_tasks, WorkFunction work, void* data) { + if (num_threads_ > 0) + DispatchMany(num_tasks, work, data); + else + DispatchHere(num_tasks, work, data); +} + diff --git a/native_client_sdk/src/libraries/sdk_util/thread_pool.h b/native_client_sdk/src/libraries/sdk_util/thread_pool.h new file mode 100644 index 0000000000..760b3f715d --- /dev/null +++ b/native_client_sdk/src/libraries/sdk_util/thread_pool.h @@ -0,0 +1,45 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Simple thread pool class + +#ifndef LIBRARIES_SDK_UTIL_THREAD_POOL_H_ +#define LIBRARIES_SDK_UTIL_THREAD_POOL_H_ + +#include <pthread.h> +#include <semaphore.h> + +// typdef helper for work function +typedef void (*WorkFunction)(int task_index, void* data); + +// ThreadPool is a class to manage num_threads and assign +// them num_tasks of work at a time. Each call +// to Dispatch(..) will block until all tasks complete. +// If 0 is passed in for num_threads, all tasks will be +// issued on the dispatch thread. + +class ThreadPool { + public: + void Dispatch(int num_tasks, WorkFunction work, void* data); + explicit ThreadPool(int num_threads); + ~ThreadPool(); + private: + int DecCounter(); + void Setup(int counter, WorkFunction work, void* data); + void DispatchMany(int num_tasks, WorkFunction work, void* data); + void DispatchHere(int num_tasks, WorkFunction work, void* data); + void WorkLoop(); + static void* WorkerThreadEntry(void* data); + void PostExitAndJoinAll(); + pthread_t* threads_; + int counter_; + const int num_threads_; + bool exiting_; + void* user_data_; + WorkFunction user_work_function_; + sem_t work_sem_; + sem_t done_sem_; +}; +#endif // LIBRARIES_SDK_UTIL_THREAD_POOL_H_ + diff --git a/native_client_sdk/src/libraries/sdk_util/thread_safe_queue.h b/native_client_sdk/src/libraries/sdk_util/thread_safe_queue.h new file mode 100644 index 0000000000..545b7c4589 --- /dev/null +++ b/native_client_sdk/src/libraries/sdk_util/thread_safe_queue.h @@ -0,0 +1,63 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_SDK_UTIL_THREAD_SAFE_QUEUE_H_ +#define LIBRARIES_SDK_UTIL_THREAD_SAFE_QUEUE_H_ + +#include <pthread.h> + +#include <list> + +#include "sdk_util/auto_lock.h" +#include "sdk_util/macros.h" + + +// ThreadSafeQueue +// +// A simple template to support multithreaded and optionally blocking access +// to a Queue of object pointers. +// +template<class T> class ThreadSafeQueue { + public: + ThreadSafeQueue() { + pthread_mutex_init(&mutex_, NULL); + pthread_cond_init(&cond_, NULL); + } + + ~ThreadSafeQueue() { + pthread_mutex_destroy(&mutex_); + pthread_cond_destroy(&cond_); + } + + void Enqueue(T* item) { + AutoLock lock(&mutex_); + list_.push_back(item); + + pthread_cond_signal(&cond_); + } + + T* Dequeue(bool block) { + AutoLock lock(&mutex_); + + // If blocking enabled, wait until we queue is non-empty + if (block) { + while (list_.empty()) pthread_cond_wait(&cond_, &mutex_); + } + + if (list_.empty()) return NULL; + + T* item = list_.front(); + list_.pop_front(); + return item; + } + + private: + std::list<T*> list_; + pthread_cond_t cond_; + pthread_mutex_t mutex_; + DISALLOW_COPY_AND_ASSIGN(ThreadSafeQueue); +}; + +#endif // LIBRARIES_SDK_UTIL_THREAD_SAFE_QUEUE_H_ + diff --git a/native_client_sdk/src/tools/create_nmf.py b/native_client_sdk/src/tools/create_nmf.py index 5cf2e1472d..a9aad544cb 100755 --- a/native_client_sdk/src/tools/create_nmf.py +++ b/native_client_sdk/src/tools/create_nmf.py @@ -610,10 +610,16 @@ def GetDefaultLibPath(config): osname = getos.GetPlatform() libpath = [ + # Core toolchain libraries 'toolchain/%s_x86_glibc/x86_64-nacl/lib' % osname, 'toolchain/%s_x86_glibc/x86_64-nacl/lib32' % osname, + # naclports installed libraries + 'toolchain/%s_x86_glibc/x86_64-nacl/usr/lib' % osname, + 'toolchain/%s_x86_glibc/i686-nacl/usr/lib' % osname, + # SDK bundle libraries 'lib/glibc_x86_32/%s' % config, 'lib/glibc_x86_64/%s' % config, + # naclports bundle libraries 'ports/lib/glibc_x86_32/%s' % config, 'ports/lib/glibc_x86_64/%s' % config, ] @@ -666,10 +672,10 @@ def main(argv): DebugPrint.debug_mode = True if options.toolchain is not None: - print 'warning: option -t/--toolchain is deprecated.' + sys.stderr.write('warning: option -t/--toolchain is deprecated.\n') if len(args) < 1: - raise Error('No nexe files specified. See --help for more info') + parser.error('No nexe files specified. See --help for more info') canonicalized = ParseExtraFiles(options.extra_files, sys.stderr) if canonicalized is None: @@ -679,7 +685,7 @@ def main(argv): for ren in options.name: parts = ren.split(',') if len(parts) != 2: - raise Error('Expecting --name=<orig_arch.so>,<new_name.so>') + parser.error('Expecting --name=<orig_arch.so>,<new_name.so>') remap[parts[0]] = parts[1] if options.path_prefix: @@ -689,9 +695,9 @@ def main(argv): for libpath in options.lib_path: if not os.path.exists(libpath): - raise Error('Specified library path does not exist: %s' % libpath) - if not os.path.isdir(libpath): - raise Error('Specified library is not a directory: %s' % libpath) + sys.stderr.write('Specified library path does not exist: %s\n' % libpath) + elif not os.path.isdir(libpath): + sys.stderr.write('Specified library is not a directory: %s\n' % libpath) if not options.no_default_libpath: # Add default libraries paths to the end of the search path. diff --git a/native_client_sdk/src/tools/host_vc.mk b/native_client_sdk/src/tools/host_vc.mk index 945fee3892..d41629b22d 100644 --- a/native_client_sdk/src/tools/host_vc.mk +++ b/native_client_sdk/src/tools/host_vc.mk @@ -27,13 +27,13 @@ $(error Unable to find cl.exe in PATH while building Windows host build) endif -ifeq ('Debug','$(CONFIG)') -WIN_OPT_FLAGS ?= /Od /MTd /Z7 -D NACL_SDK_DEBUG -else +ifeq ($(CONFIG),Release) WIN_OPT_FLAGS ?= /O2 /MT /Z7 +else +WIN_OPT_FLAGS ?= /Od /MTd /Z7 -DNACL_SDK_DEBUG endif -WIN_FLAGS ?= -D WIN32 -D _WIN32 -D PTW32_STATIC_LIB +WIN_FLAGS ?= -DWIN32 -D_WIN32 -DPTW32_STATIC_LIB # diff --git a/native_client_sdk/src/tools/nacl_gcc.mk b/native_client_sdk/src/tools/nacl_gcc.mk index 1460637e20..a68d230fde 100644 --- a/native_client_sdk/src/tools/nacl_gcc.mk +++ b/native_client_sdk/src/tools/nacl_gcc.mk @@ -47,13 +47,14 @@ ARM_NM ?= $(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-nm # Architecture-specific flags -X86_32_CFLAGS ?= -DNACL_ARCH=x86_32 -X86_64_CFLAGS ?= -DNACL_ARCH=x86_64 -ARM_CFLAGS ?= -DNACL_ARCH=arm +X86_32_CFLAGS ?= +X86_64_CFLAGS ?= +ARM_CFLAGS ?= + +X86_32_CXXFLAGS ?= +X86_64_CXXFLAGS ?= +ARM_CXXFLAGS ?= -X86_32_CXXFLAGS ?= -DNACL_ARCH=x86_32 -X86_64_CXXFLAGS ?= -DNACL_ARCH=x86_64 -ARM_CXXFLAGS ?= -DNACL_ARCH=arm # # Compile Macro |