Lib: Rework the API for cancelling a parse

Also, use beta on CI until atomic::AtomicU32 lands in stable.
This commit is contained in:
Max Brunsfeld 2019-03-15 16:10:45 -07:00
parent 55dd2330ab
commit 0ae304f582
9 changed files with 82 additions and 23 deletions

View file

@ -5,8 +5,8 @@ install:
# Install rust
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- IF "%PLATFORM%" == "x86" rustup-init -y --default-toolchain stable --default-host i686-pc-windows-msvc
- IF "%PLATFORM%" == "x64" rustup-init -y --default-toolchain stable --default-host x86_64-pc-windows-msvc
- IF "%PLATFORM%" == "x86" rustup-init -y --default-toolchain beta --default-host i686-pc-windows-msvc
- IF "%PLATFORM%" == "x64" rustup-init -y --default-toolchain beta --default-host x86_64-pc-windows-msvc
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -vV
- cargo -vV

View file

@ -1,6 +1,6 @@
language: rust
rust:
- stable
- beta
os:
- linux

2
Cargo.lock generated
View file

@ -1,3 +1,5 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.6.9"

View file

@ -1,6 +1,7 @@
use super::helpers::edits::{perform_edit, Edit, ReadRecorder};
use super::helpers::fixtures::{get_language, get_test_language};
use crate::generate::generate_parser_for_grammar;
use std::sync::atomic::{AtomicU32, Ordering};
use std::{thread, time};
use tree_sitter::{InputEdit, LogType, Parser, Point, Range};
@ -81,7 +82,11 @@ fn test_parsing_with_debug_graph_enabled() {
.lines()
.map(|l| l.expect("Failed to read line from graph log"));
for line in log_reader {
assert!(!has_zero_indexed_row(&line), "Graph log output includes zero-indexed row: {}", line);
assert!(
!has_zero_indexed_row(&line),
"Graph log output includes zero-indexed row: {}",
line
);
}
}
@ -296,6 +301,38 @@ fn test_parsing_on_multiple_threads() {
assert_eq!(child_count_differences, &[1, 2, 3, 4]);
}
#[test]
fn test_parsing_cancelled_by_another_thread() {
let cancellation_flag = AtomicU32::new(0);
let mut parser = Parser::new();
parser.set_language(get_language("javascript")).unwrap();
unsafe { parser.set_cancellation_flag(Some(&cancellation_flag)) };
let parse_thread = thread::spawn(move || {
// Infinite input
parser.parse_with(
&mut |offset, _| {
if offset == 0 {
b" ["
} else {
b"0,"
}
},
None,
)
});
let cancel_thread = thread::spawn(move || {
thread::sleep(time::Duration::from_millis(80));
cancellation_flag.store(1, Ordering::Relaxed);
});
cancel_thread.join().unwrap();
let tree = parse_thread.join().unwrap();
assert!(tree.is_none());
}
// Timeouts
#[test]

View file

@ -136,10 +136,10 @@ extern "C" {
) -> *mut TSTree;
}
extern "C" {
pub fn ts_parser_enabled(arg1: *const TSParser) -> bool;
pub fn ts_parser_cancellation_flag(arg1: *const TSParser) -> *const u32;
}
extern "C" {
pub fn ts_parser_set_enabled(arg1: *mut TSParser, arg2: bool);
pub fn ts_parser_set_cancellation_flag(arg1: *mut TSParser, arg2: *const u32);
}
extern "C" {
pub fn ts_parser_timeout_micros(arg1: *const TSParser) -> u64;

View file

@ -13,13 +13,10 @@ use regex::Regex;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomData;
use std::os::raw::{c_char, c_void};
use std::ptr;
use std::slice;
use std::str;
use std::u16;
use std::sync::atomic::AtomicU32;
use std::{fmt, ptr, slice, str, u16};
pub const PARSER_HEADER: &'static str = include_str!("../include/tree_sitter/parser.h");
@ -338,6 +335,18 @@ impl Parser {
ffi::ts_parser_set_included_ranges(self.0, ts_ranges.as_ptr(), ts_ranges.len() as u32)
};
}
pub unsafe fn cancellation_flag(&self) -> Option<&AtomicU32> {
(ffi::ts_parser_cancellation_flag(self.0) as *const AtomicU32).as_ref()
}
pub unsafe fn set_cancellation_flag(&self, flag: Option<&AtomicU32>) {
if let Some(flag) = flag {
ffi::ts_parser_set_cancellation_flag(self.0, flag as *const AtomicU32 as *const u32);
} else {
ffi::ts_parser_set_cancellation_flag(self.0, ptr::null());
}
}
}
impl Drop for Parser {

View file

@ -88,8 +88,8 @@ void ts_parser_halt_on_error(TSParser *, bool);
TSTree *ts_parser_parse(TSParser *, const TSTree *, TSInput);
TSTree *ts_parser_parse_string(TSParser *, const TSTree *, const char *, uint32_t);
TSTree *ts_parser_parse_string_encoding(TSParser *, const TSTree *, const char *, uint32_t, TSInputEncoding);
bool ts_parser_enabled(const TSParser *);
void ts_parser_set_enabled(TSParser *, bool);
const uint32_t *ts_parser_cancellation_flag(const TSParser *);
void ts_parser_set_cancellation_flag(TSParser *, const uint32_t *);
uint64_t ts_parser_timeout_micros(const TSParser *);
void ts_parser_set_timeout_micros(TSParser *, uint64_t);
void ts_parser_reset(TSParser *);

View file

@ -7,6 +7,10 @@
#include <windows.h>
static inline uint32_t atomic_load(const volatile uint32_t *p) {
return *p;
}
static inline uint32_t atomic_inc(volatile uint32_t *p) {
return InterlockedIncrement(p);
}
@ -17,6 +21,10 @@ static inline uint32_t atomic_dec(volatile uint32_t *p) {
#else
static inline uint32_t atomic_load(const volatile uint32_t *p) {
return __atomic_load_n(p, __ATOMIC_RELAXED);
}
static inline uint32_t atomic_inc(volatile uint32_t *p) {
return __sync_add_and_fetch(p, 1u);
}

View file

@ -6,6 +6,7 @@
#include "tree_sitter/api.h"
#include "./alloc.h"
#include "./array.h"
#include "./atomic.h"
#include "./clock.h"
#include "./error_costs.h"
#include "./get_changed_ranges.h"
@ -69,7 +70,7 @@ struct TSParser {
uint64_t clock_limit;
uint64_t start_clock;
unsigned operation_count;
volatile bool enabled;
const volatile uint32_t *cancellation_flag;
bool halt_on_error;
Subtree old_tree;
TSRangeArray included_range_differences;
@ -1283,9 +1284,12 @@ static bool ts_parser__advance(
}
for (;;) {
if (!self->enabled || ++self->operation_count == OP_COUNT_PER_TIMEOUT_CHECK) {
if (++self->operation_count == OP_COUNT_PER_TIMEOUT_CHECK) {
self->operation_count = 0;
if ((uint64_t)(get_clock() - self->start_clock) > self->clock_limit) {
if (
(self->cancellation_flag && !atomic_load(self->cancellation_flag)) ||
(self->clock_limit && get_clock() - self->start_clock > self->clock_limit)
) {
ts_subtree_release(&self->tree_pool, lookahead);
return false;
}
@ -1508,8 +1512,8 @@ TSParser *ts_parser_new() {
self->reusable_node = reusable_node_new();
self->dot_graph_file = NULL;
self->halt_on_error = false;
self->enabled = true;
self->clock_limit = UINT64_MAX;
self->cancellation_flag = NULL;
self->clock_limit = 0;
self->start_clock = 0;
self->operation_count = 0;
self->old_tree = NULL_SUBTREE;
@ -1585,12 +1589,12 @@ void ts_parser_halt_on_error(TSParser *self, bool should_halt_on_error) {
self->halt_on_error = should_halt_on_error;
}
bool ts_parser_enabled(const TSParser *self) {
return self->enabled;
const uint32_t *ts_parser_cancellation_flag(const TSParser *self) {
return (const uint32_t *)self->cancellation_flag;
}
void ts_parser_set_enabled(TSParser *self, bool enabled) {
self->enabled = enabled;
void ts_parser_set_cancellation_flag(TSParser *self, const uint32_t *flag) {
self->cancellation_flag = (const volatile uint32_t *)flag;
}
uint64_t ts_parser_timeout_micros(const TSParser *self) {
@ -1599,7 +1603,6 @@ uint64_t ts_parser_timeout_micros(const TSParser *self) {
void ts_parser_set_timeout_micros(TSParser *self, uint64_t timeout_micros) {
self->clock_limit = timeout_micros * get_clocks_per_second() / 1000000;
if (self->clock_limit == 0) self->clock_limit = UINT64_MAX;
}
void ts_parser_set_included_ranges(TSParser *self, const TSRange *ranges, uint32_t count) {